blob: d5b435d0acbe1fea60b0d9ef2af522bfc8cc7eca [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
cristy76ce6e12013-04-05 14:33:38 +000043#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/blob-private.h"
47#include "MagickCore/cache.h"
48#include "MagickCore/color.h"
49#include "MagickCore/color-private.h"
50#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/constitute.h"
53#include "MagickCore/delegate.h"
54#include "MagickCore/draw.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/geometry.h"
58#include "MagickCore/image.h"
59#include "MagickCore/image-private.h"
60#include "MagickCore/list.h"
61#include "MagickCore/magick.h"
62#include "MagickCore/memory_.h"
63#include "MagickCore/monitor.h"
64#include "MagickCore/monitor-private.h"
65#include "MagickCore/option.h"
66#include "MagickCore/pixel-accessor.h"
67#include "MagickCore/profile.h"
68#include "MagickCore/property.h"
69#include "MagickCore/resource_.h"
70#include "MagickCore/quantum-private.h"
71#include "MagickCore/static.h"
72#include "MagickCore/string_.h"
73#include "MagickCore/module.h"
74#include "MagickCore/token.h"
75#include "MagickCore/transform.h"
76#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000077
78/*
79 Forward declarations.
80*/
81static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +000082 WritePCLImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000083
84/*
85%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86% %
87% %
88% %
89% I s P C L %
90% %
91% %
92% %
93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94%
95% IsPCL() returns MagickTrue if the image format type, identified by the
96% magick string, is PCL.
97%
98% The format of the IsPCL method is:
99%
100% MagickBooleanType IsPCL(const unsigned char *magick,const size_t length)
101%
102% A description of each parameter follows:
103%
104% o magick: compare image format pattern against these bytes.
105%
106% o length: Specifies the length of the magick string.
107%
108*/
109static MagickBooleanType IsPCL(const unsigned char *magick,const size_t length)
110{
111 if (length < 4)
112 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000113 if (memcmp(magick,"\033E\033&",4) == 0)
114 return(MagickFalse);
cristy7dca4892010-09-21 16:10:30 +0000115 if (memcmp(magick,"\033E\033",3) == 0)
116 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +0000117 return(MagickFalse);
118}
119
120/*
121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122% %
123% %
124% %
125% R e a d P C L I m a g e %
126% %
127% %
128% %
129%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
130%
131% ReadPCLImage() reads a Printer Control Language image file and returns it.
132% It allocates the memory necessary for the new Image structure and returns a
133% pointer to the new image.
134%
135% The format of the ReadPCLImage method is:
136%
137% Image *ReadPCLImage(const ImageInfo *image_info,ExceptionInfo *exception)
138%
139% A description of each parameter follows:
140%
141% o image_info: the image info.
142%
143% o exception: return any errors or warnings in this structure.
144%
145*/
146static Image *ReadPCLImage(const ImageInfo *image_info,ExceptionInfo *exception)
147{
148#define CropBox "CropBox"
149#define DeviceCMYK "DeviceCMYK"
150#define MediaBox "MediaBox"
151#define RenderPCLText " Rendering PCL... "
152
153 char
cristy151b66d2015-04-15 10:50:31 +0000154 command[MagickPathExtent],
cristy51816562015-04-12 13:18:38 +0000155 *density,
cristy151b66d2015-04-15 10:50:31 +0000156 filename[MagickPathExtent],
157 geometry[MagickPathExtent],
cristy51816562015-04-12 13:18:38 +0000158 *options,
cristy151b66d2015-04-15 10:50:31 +0000159 input_filename[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000160
anthony1afdc7a2011-10-05 11:54:28 +0000161 const char
162 *option;
163
cristy3ed852e2009-09-05 21:47:34 +0000164 const DelegateInfo
165 *delegate_info;
166
167 Image
168 *image,
169 *next_image;
170
171 ImageInfo
172 *read_info;
173
174 MagickBooleanType
175 cmyk,
176 status;
177
178 PointInfo
179 delta;
180
181 RectangleInfo
182 bounding_box,
183 page;
184
185 register char
186 *p;
187
cristybb503372010-05-27 20:51:26 +0000188 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000189 c;
190
191 SegmentInfo
192 bounds;
193
cristybb503372010-05-27 20:51:26 +0000194 size_t
cristy3ed852e2009-09-05 21:47:34 +0000195 height,
196 width;
197
cristyaff6d802011-04-26 01:46:31 +0000198 ssize_t
199 count;
200
cristy3ed852e2009-09-05 21:47:34 +0000201 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000202 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000203 if (image_info->debug != MagickFalse)
204 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
205 image_info->filename);
206 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000207 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000208 /*
209 Open image file.
210 */
cristy9950d572011-10-01 18:22:35 +0000211 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000212 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
213 if (status == MagickFalse)
214 {
215 image=DestroyImageList(image);
216 return((Image *) NULL);
217 }
218 status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
219 if (status == MagickFalse)
220 {
221 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
222 image_info->filename);
223 image=DestroyImageList(image);
224 return((Image *) NULL);
225 }
226 /*
227 Set the page density.
228 */
229 delta.x=DefaultResolution;
230 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +0000231 if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
cristy3ed852e2009-09-05 21:47:34 +0000232 {
233 GeometryInfo
234 geometry_info;
235
236 MagickStatusType
237 flags;
238
239 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +0000240 image->resolution.x=geometry_info.rho;
241 image->resolution.y=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000242 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +0000243 image->resolution.y=image->resolution.x;
cristy3ed852e2009-09-05 21:47:34 +0000244 }
245 /*
246 Determine page geometry from the PCL media box.
247 */
248 cmyk=image->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
249 count=0;
250 (void) ResetMagickMemory(&bounding_box,0,sizeof(bounding_box));
251 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
252 (void) ResetMagickMemory(&page,0,sizeof(page));
253 (void) ResetMagickMemory(command,0,sizeof(command));
254 p=command;
255 for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
256 {
257 if (image_info->page != (char *) NULL)
258 continue;
259 /*
260 Note PCL elements.
261 */
262 *p++=(char) c;
263 if ((c != (int) '/') && (c != '\n') &&
cristy151b66d2015-04-15 10:50:31 +0000264 ((size_t) (p-command) < (MagickPathExtent-1)))
cristy3ed852e2009-09-05 21:47:34 +0000265 continue;
266 *p='\0';
267 p=command;
268 /*
269 Is this a CMYK document?
270 */
271 if (LocaleNCompare(DeviceCMYK,command,strlen(DeviceCMYK)) == 0)
272 cmyk=MagickTrue;
273 if (LocaleNCompare(CropBox,command,strlen(CropBox)) == 0)
274 {
275 /*
276 Note region defined by crop box.
277 */
278 count=(ssize_t) sscanf(command,"CropBox [%lf %lf %lf %lf",
279 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
280 if (count != 4)
281 count=(ssize_t) sscanf(command,"CropBox[%lf %lf %lf %lf",
282 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
283 }
284 if (LocaleNCompare(MediaBox,command,strlen(MediaBox)) == 0)
285 {
286 /*
287 Note region defined by media box.
288 */
289 count=(ssize_t) sscanf(command,"MediaBox [%lf %lf %lf %lf",
290 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
291 if (count != 4)
292 count=(ssize_t) sscanf(command,"MediaBox[%lf %lf %lf %lf",
293 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
294 }
295 if (count != 4)
296 continue;
297 /*
298 Set PCL render geometry.
299 */
cristybb503372010-05-27 20:51:26 +0000300 width=(size_t) floor(bounds.x2-bounds.x1+0.5);
301 height=(size_t) floor(bounds.y2-bounds.y1+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000302 if (width > page.width)
303 page.width=width;
304 if (height > page.height)
305 page.height=height;
306 }
307 (void) CloseBlob(image);
308 /*
309 Render PCL with the GhostPCL delegate.
310 */
311 if ((page.width == 0) || (page.height == 0))
312 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
313 if (image_info->page != (char *) NULL)
314 (void) ParseAbsoluteGeometry(image_info->page,&page);
cristy151b66d2015-04-15 10:50:31 +0000315 (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +0000316 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000317 if (image_info->monochrome != MagickFalse)
318 delegate_info=GetDelegateInfo("pcl:mono",(char *) NULL,exception);
319 else
320 if (cmyk != MagickFalse)
321 delegate_info=GetDelegateInfo("pcl:cmyk",(char *) NULL,exception);
322 else
323 delegate_info=GetDelegateInfo("pcl:color",(char *) NULL,exception);
324 if (delegate_info == (const DelegateInfo *) NULL)
325 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000326 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);
cristy51816562015-04-12 13:18:38 +0000330 density=AcquireString("");
331 options=AcquireString("");
cristy151b66d2015-04-15 10:50:31 +0000332 (void) FormatLocaleString(density,MagickPathExtent,"%gx%g",image->resolution.x,
cristy51816562015-04-12 13:18:38 +0000333 image->resolution.y);
cristy2a11bef2011-10-28 18:33:11 +0000334 page.width=(size_t) floor(page.width*image->resolution.x/delta.x+0.5);
cristy51816562015-04-12 13:18:38 +0000335 page.height=(size_t) floor(page.height*image->resolution.y/delta.y+0.5);
cristy151b66d2015-04-15 10:50:31 +0000336 (void) FormatLocaleString(options,MagickPathExtent,"-g%.20gx%.20g ",(double)
cristye8c25f92010-06-03 00:53:06 +0000337 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000338 image=DestroyImage(image);
339 read_info=CloneImageInfo(image_info);
340 *read_info->magick='\0';
341 if (read_info->number_scenes != 0)
342 {
343 if (read_info->number_scenes != 1)
cristy151b66d2015-04-15 10:50:31 +0000344 (void) FormatLocaleString(options,MagickPathExtent,"-dLastPage=%.20g",
cristye8c25f92010-06-03 00:53:06 +0000345 (double) (read_info->scene+read_info->number_scenes));
cristy3ed852e2009-09-05 21:47:34 +0000346 else
cristy151b66d2015-04-15 10:50:31 +0000347 (void) FormatLocaleString(options,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +0000348 "-dFirstPage=%.20g -dLastPage=%.20g",(double) read_info->scene+1,
349 (double) (read_info->scene+read_info->number_scenes));
cristy3ed852e2009-09-05 21:47:34 +0000350 read_info->number_scenes=0;
351 if (read_info->scenes != (char *) NULL)
352 *read_info->scenes='\0';
353 }
cristy092ec8d2013-04-26 13:46:22 +0000354 option=GetImageOption(image_info,"authenticate");
anthony1afdc7a2011-10-05 11:54:28 +0000355 if (option != (const char *) NULL)
cristy151b66d2015-04-15 10:50:31 +0000356 (void) FormatLocaleString(options+strlen(options),MagickPathExtent,
anthony1afdc7a2011-10-05 11:54:28 +0000357 " -sPCLPassword=%s",option);
cristy151b66d2015-04-15 10:50:31 +0000358 (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000359 (void) AcquireUniqueFilename(read_info->filename);
cristy151b66d2015-04-15 10:50:31 +0000360 (void) FormatLocaleString(command,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000361 GetDelegateCommands(delegate_info),
362 read_info->antialias != MagickFalse ? 4 : 1,
363 read_info->antialias != MagickFalse ? 4 : 1,density,options,
364 read_info->filename,input_filename);
cristy51816562015-04-12 13:18:38 +0000365 options=DestroyString(options);
366 density=DestroyString(density);
cristydfc19b62014-10-17 22:52:24 +0000367 status=ExternalDelegateCommand(MagickFalse,read_info->verbose,command,
368 (char *) NULL,exception) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000369 image=ReadImage(read_info,exception);
370 (void) RelinquishUniqueFileResource(read_info->filename);
371 (void) RelinquishUniqueFileResource(input_filename);
372 read_info=DestroyImageInfo(read_info);
373 if (image == (Image *) NULL)
374 ThrowReaderException(DelegateError,"PCLDelegateFailed");
375 if (LocaleCompare(image->magick,"BMP") == 0)
376 {
377 Image
378 *cmyk_image;
379
cristyc82a27b2011-10-21 01:07:16 +0000380 cmyk_image=ConsolidateCMYKImages(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000381 if (cmyk_image != (Image *) NULL)
382 {
383 image=DestroyImageList(image);
384 image=cmyk_image;
385 }
386 }
387 do
388 {
cristy151b66d2015-04-15 10:50:31 +0000389 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000390 image->page=page;
391 next_image=SyncNextImageInList(image);
392 if (next_image != (Image *) NULL)
393 image=next_image;
394 } while (next_image != (Image *) NULL);
395 return(GetFirstImageInList(image));
396}
397
398/*
399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400% %
401% %
402% %
403% R e g i s t e r P C L I m a g e %
404% %
405% %
406% %
407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
408%
409% RegisterPCLImage() adds attributes for the PCL image format to
410% the list of supported formats. The attributes include the image format
411% tag, a method to read and/or write the format, whether the format
412% supports the saving of more than one frame to the i file or blob,
413% whether the format supports native in-memory I/O, and a brief
414% description of the format.
415%
416% The format of the RegisterPCLImage method is:
417%
cristybb503372010-05-27 20:51:26 +0000418% size_t RegisterPCLImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000419%
420*/
cristybb503372010-05-27 20:51:26 +0000421ModuleExport size_t RegisterPCLImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000422{
423 MagickInfo
424 *entry;
425
dirk06b627a2015-04-06 18:59:17 +0000426 entry=AcquireMagickInfo("PCL","PCL","Printer Control Language");
cristy3ed852e2009-09-05 21:47:34 +0000427 entry->decoder=(DecodeImageHandler *) ReadPCLImage;
428 entry->encoder=(EncodeImageHandler *) WritePCLImage;
429 entry->magick=(IsImageFormatHandler *) IsPCL;
dirk08e9a112015-02-22 01:51:41 +0000430 entry->flags^=CoderBlobSupportFlag;
431 entry->flags^=CoderDecoderThreadSupportFlag;
432 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000433 (void) RegisterMagickInfo(entry);
434 return(MagickImageCoderSignature);
435}
436
437/*
438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439% %
440% %
441% %
442% U n r e g i s t e r P C L I m a g e %
443% %
444% %
445% %
446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447%
448% UnregisterPCLImage() removes format registrations made by the PCL module
449% from the list of supported formats.
450%
451% The format of the UnregisterPCLImage method is:
452%
453% UnregisterPCLImage(void)
454%
455*/
456ModuleExport void UnregisterPCLImage(void)
457{
458 (void) UnregisterMagickInfo("PCL");
459}
460
461/*
462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463% %
464% %
465% %
466% W r i t e P C L I m a g e %
467% %
468% %
469% %
470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471%
472% WritePCLImage() writes an image in the Page Control Language encoded
473% image format.
474%
475% The format of the WritePCLImage method is:
476%
cristy1e178e72011-08-28 19:44:34 +0000477% MagickBooleanType WritePCLImage(const ImageInfo *image_info,
478% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000479%
480% A description of each parameter follows.
481%
482% o image_info: the image info.
483%
484% o image: The image.
485%
cristy1e178e72011-08-28 19:44:34 +0000486% o exception: return any errors or warnings in this structure.
487%
cristy3ed852e2009-09-05 21:47:34 +0000488*/
489
490static size_t PCLDeltaCompressImage(const size_t length,
491 const unsigned char *previous_pixels,const unsigned char *pixels,
492 unsigned char *compress_pixels)
493{
494 int
495 delta,
496 j,
497 replacement;
498
cristybb503372010-05-27 20:51:26 +0000499 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000500 i,
501 x;
502
503 register unsigned char
504 *q;
505
506 q=compress_pixels;
cristybb503372010-05-27 20:51:26 +0000507 for (x=0; x < (ssize_t) length; )
cristy3ed852e2009-09-05 21:47:34 +0000508 {
509 j=0;
cristybb503372010-05-27 20:51:26 +0000510 for (i=0; x < (ssize_t) length; x++)
cristy3ed852e2009-09-05 21:47:34 +0000511 {
512 if (*pixels++ != *previous_pixels++)
513 {
514 i=1;
515 break;
516 }
517 j++;
518 }
cristy91e52622010-11-03 01:05:41 +0000519 while (x < (ssize_t) length)
cristy3ed852e2009-09-05 21:47:34 +0000520 {
cristy91e52622010-11-03 01:05:41 +0000521 x++;
cristy3ed852e2009-09-05 21:47:34 +0000522 if (*pixels == *previous_pixels)
523 break;
524 i++;
525 previous_pixels++;
526 pixels++;
527 }
528 if (i == 0)
529 break;
530 replacement=j >= 31 ? 31 : j;
531 j-=replacement;
532 delta=i >= 8 ? 8 : i;
533 *q++=(unsigned char) (((delta-1) << 5) | replacement);
534 if (replacement == 31)
535 {
536 for (replacement=255; j != 0; )
537 {
538 if (replacement > j)
539 replacement=j;
540 *q++=(unsigned char) replacement;
541 j-=replacement;
542 }
543 if (replacement == 255)
544 *q++='\0';
545 }
546 for (pixels-=i; i != 0; )
547 {
548 for (i-=delta; delta != 0; delta--)
549 *q++=(*pixels++);
550 if (i == 0)
551 break;
552 delta=i;
553 if (i >= 8)
554 delta=8;
555 *q++=(unsigned char) ((delta-1) << 5);
556 }
557 }
558 return((size_t) (q-compress_pixels));
559}
560
561static size_t PCLPackbitsCompressImage(const size_t length,
562 const unsigned char *pixels,unsigned char *compress_pixels)
563{
564 int
565 count;
566
cristybb503372010-05-27 20:51:26 +0000567 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000568 x;
569
570 register unsigned char
571 *q;
572
cristyaff6d802011-04-26 01:46:31 +0000573 ssize_t
574 j;
575
cristy3ed852e2009-09-05 21:47:34 +0000576 unsigned char
577 packbits[128];
578
579 /*
580 Compress pixels with Packbits encoding.
581 */
582 q=compress_pixels;
cristybb503372010-05-27 20:51:26 +0000583 for (x=(ssize_t) length; x != 0; )
cristy3ed852e2009-09-05 21:47:34 +0000584 {
585 switch (x)
586 {
587 case 1:
588 {
589 x--;
590 *q++=0;
591 *q++=(*pixels);
592 break;
593 }
594 case 2:
595 {
596 x-=2;
597 *q++=1;
598 *q++=(*pixels);
599 *q++=pixels[1];
600 break;
601 }
602 case 3:
603 {
604 x-=3;
605 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
606 {
607 *q++=(unsigned char) ((256-3)+1);
608 *q++=(*pixels);
609 break;
610 }
611 *q++=2;
612 *q++=(*pixels);
613 *q++=pixels[1];
614 *q++=pixels[2];
615 break;
616 }
617 default:
618 {
619 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
620 {
621 /*
622 Packed run.
623 */
624 count=3;
cristybb503372010-05-27 20:51:26 +0000625 while (((ssize_t) count < x) && (*pixels == *(pixels+count)))
cristy3ed852e2009-09-05 21:47:34 +0000626 {
627 count++;
628 if (count >= 127)
629 break;
630 }
631 x-=count;
632 *q++=(unsigned char) ((256-count)+1);
633 *q++=(*pixels);
634 pixels+=count;
635 break;
636 }
637 /*
638 Literal run.
639 */
640 count=0;
641 while ((*(pixels+count) != *(pixels+count+1)) ||
642 (*(pixels+count+1) != *(pixels+count+2)))
643 {
644 packbits[count+1]=pixels[count];
645 count++;
cristybb503372010-05-27 20:51:26 +0000646 if (((ssize_t) count >= (x-3)) || (count >= 127))
cristy3ed852e2009-09-05 21:47:34 +0000647 break;
648 }
649 x-=count;
650 *packbits=(unsigned char) (count-1);
cristybb503372010-05-27 20:51:26 +0000651 for (j=0; j <= (ssize_t) count; j++)
cristy3ed852e2009-09-05 21:47:34 +0000652 *q++=packbits[j];
653 pixels+=count;
654 break;
655 }
656 }
657 }
658 *q++=128; /* EOD marker */
659 return((size_t) (q-compress_pixels));
660}
661
cristy1e178e72011-08-28 19:44:34 +0000662static MagickBooleanType WritePCLImage(const ImageInfo *image_info,Image *image,
663 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000664{
665 char
cristy151b66d2015-04-15 10:50:31 +0000666 buffer[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000667
dirk29dd80e2013-10-31 23:11:11 +0000668 CompressionType
669 compression;
670
cristy688f07b2009-09-27 15:19:13 +0000671 const char
672 *option;
673
cristy3ed852e2009-09-05 21:47:34 +0000674 MagickBooleanType
675 status;
676
677 MagickOffsetType
678 scene;
679
cristy4c08aed2011-07-01 19:47:50 +0000680 register const Quantum *p;
cristy3ed852e2009-09-05 21:47:34 +0000681
cristy4c08aed2011-07-01 19:47:50 +0000682 register ssize_t i, x;
683
684 register unsigned char *q;
685
686 size_t
687 density,
688 length,
cristyaff6d802011-04-26 01:46:31 +0000689 one,
cristy3ed852e2009-09-05 21:47:34 +0000690 packets;
691
cristyaff6d802011-04-26 01:46:31 +0000692 ssize_t
693 y;
694
cristy3ed852e2009-09-05 21:47:34 +0000695 unsigned char
696 bits_per_pixel,
697 *compress_pixels,
698 *pixels,
699 *previous_pixels;
700
cristy3ed852e2009-09-05 21:47:34 +0000701 /*
702 Open output image file.
703 */
704 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000705 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000706 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000707 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000708 if (image->debug != MagickFalse)
709 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000710 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000711 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +0000712 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000713 if (status == MagickFalse)
714 return(status);
715 density=75;
716 if (image_info->density != (char *) NULL)
717 {
718 GeometryInfo
719 geometry;
720
721 (void) ParseGeometry(image_info->density,&geometry);
cristybb503372010-05-27 20:51:26 +0000722 density=(size_t) geometry.rho;
cristy3ed852e2009-09-05 21:47:34 +0000723 }
724 scene=0;
cristyf9cca6a2010-06-04 23:49:28 +0000725 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000726 do
727 {
cristy3ed852e2009-09-05 21:47:34 +0000728 /*
729 Initialize the printer.
730 */
cristyaf8d3912014-02-21 14:50:33 +0000731 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000732 (void) WriteBlobString(image,"\033E"); /* printer reset */
733 (void) WriteBlobString(image,"\033*r3F"); /* set presentation mode */
cristy151b66d2015-04-15 10:50:31 +0000734 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*r%.20gs%.20gT",
cristye8c25f92010-06-03 00:53:06 +0000735 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000736 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +0000737 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*t%.20gR",(double)
cristye8c25f92010-06-03 00:53:06 +0000738 density);
cristy3ed852e2009-09-05 21:47:34 +0000739 (void) WriteBlobString(image,buffer);
740 (void) WriteBlobString(image,"\033&l0E"); /* top margin 0 */
dirkf1d85482015-04-06 00:36:00 +0000741 if (SetImageMonochrome(image,exception) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000742 {
743 /*
cristyfcbe33b2011-01-19 23:36:00 +0000744 Monochrome image: use default printer monochrome setup.
cristy3ed852e2009-09-05 21:47:34 +0000745 */
746 bits_per_pixel=1;
cristy3ed852e2009-09-05 21:47:34 +0000747 }
748 else
749 if (image->storage_class == DirectClass)
750 {
751 /*
752 DirectClass image.
753 */
754 bits_per_pixel=24;
755 (void) WriteBlobString(image,"\033*v6W"); /* set color mode */
756 (void) WriteBlobByte(image,0); /* RGB */
757 (void) WriteBlobByte(image,3); /* direct by pixel */
758 (void) WriteBlobByte(image,0); /* bits per index (ignored) */
759 (void) WriteBlobByte(image,8); /* bits per red component */
760 (void) WriteBlobByte(image,8); /* bits per green component */
761 (void) WriteBlobByte(image,8); /* bits per blue component */
762 }
763 else
764 {
765 /*
766 Colormapped image.
767 */
768 bits_per_pixel=8;
769 (void) WriteBlobString(image,"\033*v6W"); /* set color mode... */
770 (void) WriteBlobByte(image,0); /* RGB */
771 (void) WriteBlobByte(image,1); /* indexed by pixel */
772 (void) WriteBlobByte(image,bits_per_pixel); /* bits per index */
773 (void) WriteBlobByte(image,8); /* bits per red component */
774 (void) WriteBlobByte(image,8); /* bits per green component */
775 (void) WriteBlobByte(image,8); /* bits per blue component */
cristybb503372010-05-27 20:51:26 +0000776 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000777 {
cristy151b66d2015-04-15 10:50:31 +0000778 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +0000779 "\033*v%da%db%dc%.20gI",
780 ScaleQuantumToChar(image->colormap[i].red),
cristy3ed852e2009-09-05 21:47:34 +0000781 ScaleQuantumToChar(image->colormap[i].green),
cristye8c25f92010-06-03 00:53:06 +0000782 ScaleQuantumToChar(image->colormap[i].blue),(double) i);
cristy3ed852e2009-09-05 21:47:34 +0000783 (void) WriteBlobString(image,buffer);
784 }
cristyaff6d802011-04-26 01:46:31 +0000785 for (one=1; i < (ssize_t) (one << bits_per_pixel); i++)
cristy3ed852e2009-09-05 21:47:34 +0000786 {
cristy151b66d2015-04-15 10:50:31 +0000787 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*v%.20gI",
cristye8c25f92010-06-03 00:53:06 +0000788 (double) i);
cristy3ed852e2009-09-05 21:47:34 +0000789 (void) WriteBlobString(image,buffer);
790 }
791 }
cristy092ec8d2013-04-26 13:46:22 +0000792 option=GetImageOption(image_info,"pcl:fit-to-page");
dirk360b9862016-01-05 15:20:46 +0100793 if (IsStringTrue(option) != MagickFalse)
cristy688f07b2009-09-27 15:19:13 +0000794 (void) WriteBlobString(image,"\033*r3A");
795 else
796 (void) WriteBlobString(image,"\033*r1A"); /* start raster graphics */
cristy3ed852e2009-09-05 21:47:34 +0000797 (void) WriteBlobString(image,"\033*b0Y"); /* set y offset */
798 length=(image->columns*bits_per_pixel+7)/8;
cristy91e52622010-11-03 01:05:41 +0000799 pixels=(unsigned char *) AcquireQuantumMemory(length+1,sizeof(*pixels));
cristy3ed852e2009-09-05 21:47:34 +0000800 if (pixels == (unsigned char *) NULL)
801 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy91e52622010-11-03 01:05:41 +0000802 (void) ResetMagickMemory(pixels,0,(length+1)*sizeof(*pixels));
cristy3ed852e2009-09-05 21:47:34 +0000803 compress_pixels=(unsigned char *) NULL;
804 previous_pixels=(unsigned char *) NULL;
dirk29dd80e2013-10-31 23:11:11 +0000805
806 compression=UndefinedCompression;
807 if (image_info->compression != UndefinedCompression)
808 compression=image_info->compression;
809 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +0000810 {
811 case NoCompression:
812 {
cristy151b66d2015-04-15 10:50:31 +0000813 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b0M");
cristy3ed852e2009-09-05 21:47:34 +0000814 (void) WriteBlobString(image,buffer);
815 break;
816 }
817 case RLECompression:
818 {
819 compress_pixels=(unsigned char *) AcquireQuantumMemory(length+256,
820 sizeof(*compress_pixels));
821 if (compress_pixels == (unsigned char *) NULL)
822 {
823 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
824 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
825 }
cristy84cb9f42010-11-02 16:06:30 +0000826 (void) ResetMagickMemory(compress_pixels,0,(length+256)*
827 sizeof(*compress_pixels));
cristy151b66d2015-04-15 10:50:31 +0000828 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b2M");
cristy3ed852e2009-09-05 21:47:34 +0000829 (void) WriteBlobString(image,buffer);
830 break;
831 }
832 default:
833 {
cristy70865522011-03-30 14:36:48 +0000834 compress_pixels=(unsigned char *) AcquireQuantumMemory(3*length+256,
cristy91e52622010-11-03 01:05:41 +0000835 sizeof(*compress_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000836 if (compress_pixels == (unsigned char *) NULL)
837 {
838 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
839 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
840 }
cristy70865522011-03-30 14:36:48 +0000841 (void) ResetMagickMemory(compress_pixels,0,(3*length+256)*
cristy84cb9f42010-11-02 16:06:30 +0000842 sizeof(*compress_pixels));
cristy91e52622010-11-03 01:05:41 +0000843 previous_pixels=(unsigned char *) AcquireQuantumMemory(length+1,
cristy3ed852e2009-09-05 21:47:34 +0000844 sizeof(*previous_pixels));
845 if (previous_pixels == (unsigned char *) NULL)
846 {
847 compress_pixels=(unsigned char *) RelinquishMagickMemory(
848 compress_pixels);
849 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
850 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
851 }
cristy91e52622010-11-03 01:05:41 +0000852 (void) ResetMagickMemory(previous_pixels,0,(length+1)*
cristy84cb9f42010-11-02 16:06:30 +0000853 sizeof(*previous_pixels));
cristy151b66d2015-04-15 10:50:31 +0000854 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b3M");
cristy3ed852e2009-09-05 21:47:34 +0000855 (void) WriteBlobString(image,buffer);
856 break;
857 }
858 }
cristybb503372010-05-27 20:51:26 +0000859 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000860 {
cristy1e178e72011-08-28 19:44:34 +0000861 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000862 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000863 break;
cristy3ed852e2009-09-05 21:47:34 +0000864 q=pixels;
865 switch (bits_per_pixel)
866 {
867 case 1:
868 {
869 register unsigned char
870 bit,
871 byte;
872
873 /*
874 Monochrome image.
875 */
876 bit=0;
877 byte=0;
cristybb503372010-05-27 20:51:26 +0000878 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000879 {
880 byte<<=1;
cristyd0323222013-04-07 16:13:21 +0000881 if (GetPixelLuma(image,p) < (QuantumRange/2.0))
cristy3ed852e2009-09-05 21:47:34 +0000882 byte|=0x01;
883 bit++;
884 if (bit == 8)
885 {
886 *q++=byte;
887 bit=0;
888 byte=0;
889 }
cristyed231572011-07-14 02:18:59 +0000890 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000891 }
892 if (bit != 0)
893 *q++=byte << (8-bit);
894 break;
895 }
896 case 8:
897 {
898 /*
899 Colormapped image.
900 */
cristybb503372010-05-27 20:51:26 +0000901 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +0000902 {
903 *q++=(unsigned char) GetPixelIndex(image,p);
cristyed231572011-07-14 02:18:59 +0000904 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000905 }
cristy3ed852e2009-09-05 21:47:34 +0000906 break;
907 }
908 case 24:
909 case 32:
910 {
911 /*
912 Truecolor image.
913 */
cristybb503372010-05-27 20:51:26 +0000914 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000915 {
cristy4c08aed2011-07-01 19:47:50 +0000916 *q++=ScaleQuantumToChar(GetPixelRed(image,p));
917 *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
918 *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +0000919 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000920 }
921 break;
922 }
923 }
dirk29dd80e2013-10-31 23:11:11 +0000924 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +0000925 {
926 case NoCompression:
927 {
cristy151b66d2015-04-15 10:50:31 +0000928 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b%.20gW",
cristye8c25f92010-06-03 00:53:06 +0000929 (double) length);
cristy3ed852e2009-09-05 21:47:34 +0000930 (void) WriteBlobString(image,buffer);
931 (void) WriteBlob(image,length,pixels);
932 break;
933 }
934 case RLECompression:
935 {
cristyaff6d802011-04-26 01:46:31 +0000936 packets=PCLPackbitsCompressImage(length,pixels,compress_pixels);
cristy151b66d2015-04-15 10:50:31 +0000937 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b%.20gW",
cristye8c25f92010-06-03 00:53:06 +0000938 (double) packets);
cristy3ed852e2009-09-05 21:47:34 +0000939 (void) WriteBlobString(image,buffer);
940 (void) WriteBlob(image,packets,compress_pixels);
941 break;
942 }
943 default:
944 {
945 if (y == 0)
cristybb503372010-05-27 20:51:26 +0000946 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000947 previous_pixels[i]=(~pixels[i]);
948 packets=PCLDeltaCompressImage(length,previous_pixels,pixels,
949 compress_pixels);
cristy151b66d2015-04-15 10:50:31 +0000950 (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b%.20gW",
cristye8c25f92010-06-03 00:53:06 +0000951 (double) packets);
cristy3ed852e2009-09-05 21:47:34 +0000952 (void) WriteBlobString(image,buffer);
953 (void) WriteBlob(image,packets,compress_pixels);
954 (void) CopyMagickMemory(previous_pixels,pixels,length*
955 sizeof(*pixels));
956 break;
957 }
958 }
959 }
cristy8bc68ee2009-09-24 18:39:29 +0000960 (void) WriteBlobString(image,"\033*rB"); /* end graphics */
dirk29dd80e2013-10-31 23:11:11 +0000961 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +0000962 {
963 case NoCompression:
964 break;
965 case RLECompression:
966 {
967 compress_pixels=(unsigned char *) RelinquishMagickMemory(
968 compress_pixels);
969 break;
970 }
971 default:
972 {
973 previous_pixels=(unsigned char *) RelinquishMagickMemory(
974 previous_pixels);
975 compress_pixels=(unsigned char *) RelinquishMagickMemory(
976 compress_pixels);
977 break;
978 }
979 }
980 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
981 if (GetNextImageInList(image) == (Image *) NULL)
982 break;
983 image=SyncNextImageInList(image);
984 status=SetImageProgress(image,SaveImagesTag,scene++,
985 GetImageListLength(image));
986 if (status == MagickFalse)
987 break;
988 } while (image_info->adjoin != MagickFalse);
989 (void) WriteBlobString(image,"\033E");
990 (void) CloseBlob(image);
991 return(MagickTrue);
992}