]> granicus.if.org Git - imagemagick/blob - coders/fax.c
Added support for GROUP4 compression to the FAX coder.
[imagemagick] / coders / fax.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            FFFFF   AAA   X   X                              %
7 %                            F      A   A   X X                               %
8 %                            FFF    AAAAA    X                                %
9 %                            F      A   A   X X                               %
10 %                            F      A   A  X   X                              %
11 %                                                                             %
12 %                                                                             %
13 %                   Read/Write Group 3 Fax Image Format                       %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2016 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/colormap.h"
47 #include "MagickCore/colorspace.h"
48 #include "MagickCore/colorspace-private.h"
49 #include "MagickCore/constitute.h"
50 #include "MagickCore/exception.h"
51 #include "MagickCore/exception-private.h"
52 #include "MagickCore/compress.h"
53 #include "MagickCore/image.h"
54 #include "MagickCore/image-private.h"
55 #include "MagickCore/list.h"
56 #include "MagickCore/magick.h"
57 #include "MagickCore/memory_.h"
58 #include "MagickCore/monitor.h"
59 #include "MagickCore/monitor-private.h"
60 #include "MagickCore/quantum-private.h"
61 #include "MagickCore/resource_.h"
62 #include "MagickCore/static.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/module.h"
65 \f
66 /*
67   Forward declarations.
68 */
69 static MagickBooleanType
70   WriteFAXImage(const ImageInfo *,Image *,ExceptionInfo *);
71 \f
72 /*
73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74 %                                                                             %
75 %                                                                             %
76 %                                                                             %
77 %   I s F A X                                                                 %
78 %                                                                             %
79 %                                                                             %
80 %                                                                             %
81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 %
83 %  IsFAX() returns MagickTrue if the image format type, identified by the
84 %  magick string, is FAX.
85 %
86 %  The format of the IsFAX method is:
87 %
88 %      MagickBooleanType IsFAX(const unsigned char *magick,const size_t length)
89 %
90 %  A description of each parameter follows:
91 %
92 %    o magick: compare image format pattern against these bytes.
93 %
94 %    o length: Specifies the length of the magick string.
95 %
96 %
97 */
98 static MagickBooleanType IsFAX(const unsigned char *magick,const size_t length)
99 {
100   if (length < 4)
101     return(MagickFalse);
102   if (LocaleNCompare((char *) magick,"DFAX",4) == 0)
103     return(MagickTrue);
104   return(MagickFalse);
105 }
106 \f
107 /*
108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109 %                                                                             %
110 %                                                                             %
111 %                                                                             %
112 %   R e a d F A X I m a g e                                                   %
113 %                                                                             %
114 %                                                                             %
115 %                                                                             %
116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117 %
118 %  ReadFAXImage() reads a Group 3 FAX image file and returns it.  It
119 %  allocates the memory necessary for the new Image structure and returns a
120 %  pointer to the new image.
121 %
122 %  The format of the ReadFAXImage method is:
123 %
124 %      Image *ReadFAXImage(const ImageInfo *image_info,ExceptionInfo *exception)
125 %
126 %  A description of each parameter follows:
127 %
128 %    o image_info: the image info.
129 %
130 %    o exception: return any errors or warnings in this structure.
131 %
132 */
133 static Image* FaxReadG3(Image *image,ExceptionInfo *exception)
134 {
135   MagickBooleanType
136     status;
137
138   status=HuffmanDecodeImage(image,exception);
139   if (status == MagickFalse)
140     ThrowFileException(exception,CorruptImageError,"UnableToReadImageData",
141       image->filename);
142   if (EOFBlob(image) != MagickFalse)
143     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
144       image->filename);
145   (void) CloseBlob(image);
146   return(GetFirstImageInList(image));
147 }
148
149 static Image* FaxReadG4(Image *image,const ImageInfo *image_info,
150   ExceptionInfo *exception)
151 {
152   char
153     filename[MagickPathExtent];
154
155   ImageInfo
156     *read_info;
157
158   MagickBooleanType
159     status;
160
161   filename[0]='\0';
162   if (ImageToFile(image,filename,exception) == MagickFalse)
163     ThrowImageException(FileOpenError,"UnableToCreateTemporaryFile");
164   (void) CloseBlob(image);
165   image=DestroyImage(image);
166   read_info=CloneImageInfo(image_info);
167   SetImageInfoBlob(read_info,(void *) NULL,0);
168   (void) FormatLocaleString(read_info->filename,MagickPathExtent,"group4:%s",
169     filename);
170   read_info->orientation=TopLeftOrientation;
171   image=ReadImage(read_info,exception);
172   if (image != (Image *) NULL)
173     {
174       (void) CopyMagickString(image->filename,image_info->filename,
175         MagickPathExtent);
176       (void) CopyMagickString(image->magick_filename,image_info->filename,
177         MagickPathExtent);
178       (void) CopyMagickString(image->magick,"G4",MagickPathExtent);
179     }
180   read_info=DestroyImageInfo(read_info);
181   (void) RelinquishUniqueFileResource(filename);
182   return(GetFirstImageInList(image));
183 }
184
185 static Image *ReadFAXImage(const ImageInfo *image_info,ExceptionInfo *exception)
186 {
187   Image
188     *image;
189
190   MagickBooleanType
191     status;
192
193   /*
194     Open image file.
195   */
196   assert(image_info != (const ImageInfo *) NULL);
197   assert(image_info->signature == MagickCoreSignature);
198   if (image_info->debug != MagickFalse)
199     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
200       image_info->filename);
201   assert(exception != (ExceptionInfo *) NULL);
202   assert(exception->signature == MagickCoreSignature);
203   image=AcquireImage(image_info,exception);
204   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
205   if (status == MagickFalse)
206     {
207       image=DestroyImageList(image);
208       return((Image *) NULL);
209     }
210   /*
211     Initialize image structure.
212   */
213   image->storage_class=PseudoClass;
214   if (image->columns == 0)
215     image->columns=2592;
216   if (image->rows == 0)
217     image->rows=3508;
218   image->depth=8;
219   if (AcquireImageColormap(image,2,exception) == MagickFalse)
220     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
221   /*
222     Monochrome colormap.
223   */
224   image->colormap[0].red=QuantumRange;
225   image->colormap[0].green=QuantumRange;
226   image->colormap[0].blue=QuantumRange;
227   image->colormap[1].red=(Quantum) 0;
228   image->colormap[1].green=(Quantum) 0;
229   image->colormap[1].blue=(Quantum) 0;
230   if (image_info->ping != MagickFalse)
231     {
232       (void) CloseBlob(image);
233       return(GetFirstImageInList(image));
234     }
235   status=SetImageExtent(image,image->columns,image->rows,exception);
236   if (status == MagickFalse)
237     return(DestroyImageList(image));
238   if (LocaleCompare(image_info->magick,"G4") == 0)
239     return(FaxReadG4(image,image_info,exception));
240   else
241     return(FaxReadG3(image,exception));
242 }
243 \f
244 /*
245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246 %                                                                             %
247 %                                                                             %
248 %                                                                             %
249 %   R e g i s t e r F A X I m a g e                                           %
250 %                                                                             %
251 %                                                                             %
252 %                                                                             %
253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
254 %
255 %  RegisterFAXImage() adds attributes for the FAX image format to
256 %  the list of supported formats.  The attributes include the image format
257 %  tag, a method to read and/or write the format, whether the format
258 %  supports the saving of more than one frame to the same file or blob,
259 %  whether the format supports native in-memory I/O, and a brief
260 %  description of the format.
261 %
262 %  The format of the RegisterFAXImage method is:
263 %
264 %      size_t RegisterFAXImage(void)
265 %
266 */
267 ModuleExport size_t RegisterFAXImage(void)
268 {
269   MagickInfo
270     *entry;
271
272   static const char
273     *Note=
274     {
275       "FAX machines use non-square pixels which are 1.5 times wider than\n"
276       "they are tall but computer displays use square pixels, therefore\n"
277       "FAX images may appear to be narrow unless they are explicitly\n"
278       "resized using a geometry of \"150x100%\".\n"
279     };
280
281   entry=AcquireMagickInfo("FAX","FAX","Group 3 FAX");
282   entry->decoder=(DecodeImageHandler *) ReadFAXImage;
283   entry->encoder=(EncodeImageHandler *) WriteFAXImage;
284   entry->magick=(IsImageFormatHandler *) IsFAX;
285   entry->note=ConstantString(Note);
286   (void) RegisterMagickInfo(entry);
287   entry=AcquireMagickInfo("FAX","G3","Group 3 FAX");
288   entry->decoder=(DecodeImageHandler *) ReadFAXImage;
289   entry->encoder=(EncodeImageHandler *) WriteFAXImage;
290   entry->magick=(IsImageFormatHandler *) IsFAX;
291   entry->flags^=CoderAdjoinFlag;
292   (void) RegisterMagickInfo(entry);
293   entry=AcquireMagickInfo("FAX","G4","Group 4 FAX");
294   entry->decoder=(DecodeImageHandler *) ReadFAXImage;
295   entry->encoder=(EncodeImageHandler *) WriteFAXImage;
296   entry->magick=(IsImageFormatHandler *) IsFAX;
297   entry->flags^=CoderAdjoinFlag;
298   (void) RegisterMagickInfo(entry);
299   return(MagickImageCoderSignature);
300 }
301 \f
302 /*
303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304 %                                                                             %
305 %                                                                             %
306 %                                                                             %
307 %   U n r e g i s t e r F A X I m a g e                                       %
308 %                                                                             %
309 %                                                                             %
310 %                                                                             %
311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
312 %
313 %  UnregisterFAXImage() removes format registrations made by the
314 %  FAX module from the list of supported formats.
315 %
316 %  The format of the UnregisterFAXImage method is:
317 %
318 %      UnregisterFAXImage(void)
319 %
320 */
321 ModuleExport void UnregisterFAXImage(void)
322 {
323   (void) UnregisterMagickInfo("FAX");
324   (void) UnregisterMagickInfo("G3");
325   (void) UnregisterMagickInfo("G4");
326 }
327 \f
328 /*
329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330 %                                                                             %
331 %                                                                             %
332 %                                                                             %
333 %   W r i t e F A X I m a g e                                                 %
334 %                                                                             %
335 %                                                                             %
336 %                                                                             %
337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338 %
339 %  WriteFAXImage() writes an image to a file in 1 dimensional Huffman encoded
340 %  format.
341 %
342 %  The format of the WriteFAXImage method is:
343 %
344 %      MagickBooleanType WriteFAXImage(const ImageInfo *image_info,
345 %        Image *image,ExceptionInfo *exception)
346 %
347 %  A description of each parameter follows.
348 %
349 %    o image_info: the image info.
350 %
351 %    o image:  The image.
352 %
353 %    o exception: return any errors or warnings in this structure.
354 %
355 */
356 static MagickBooleanType WriteFAXImage(const ImageInfo *image_info,Image *image,
357   ExceptionInfo *exception)
358 {
359   ImageInfo
360     *write_info;
361
362   MagickBooleanType
363     status;
364
365   MagickOffsetType
366     scene;
367
368   /*
369     Open output image file.
370   */
371   assert(image_info != (const ImageInfo *) NULL);
372   assert(image_info->signature == MagickCoreSignature);
373   assert(image != (Image *) NULL);
374   assert(image->signature == MagickCoreSignature);
375   if (image->debug != MagickFalse)
376     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
377   assert(exception != (ExceptionInfo *) NULL);
378   assert(exception->signature == MagickCoreSignature);
379   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
380   if (status == MagickFalse)
381     return(status);
382   write_info=CloneImageInfo(image_info);
383   (void) CopyMagickString(write_info->magick,"FAX",MagickPathExtent);
384   scene=0;
385   do
386   {
387     /*
388       Convert MIFF to monochrome.
389     */
390     (void) TransformImageColorspace(image,sRGBColorspace,exception);
391     status=HuffmanEncodeImage(write_info,image,image,exception);
392     if (GetNextImageInList(image) == (Image *) NULL)
393       break;
394     image=SyncNextImageInList(image);
395     status=SetImageProgress(image,SaveImagesTag,scene++,
396       GetImageListLength(image));
397     if (status == MagickFalse)
398       break;
399   } while (write_info->adjoin != MagickFalse);
400   write_info=DestroyImageInfo(write_info);
401   (void) CloseBlob(image);
402   return(status);
403 }