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
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
66/*
67  Forward declarations.
68*/
69static MagickBooleanType
70  WriteFAXImage(const ImageInfo *,Image *,ExceptionInfo *);
71
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*/
98static 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
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*/
133static 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
149static 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  filename[0]='\0';
159  if (ImageToFile(image,filename,exception) == MagickFalse)
160    ThrowImageException(FileOpenError,"UnableToCreateTemporaryFile");
161  (void) CloseBlob(image);
162  image=DestroyImage(image);
163  read_info=CloneImageInfo(image_info);
164  SetImageInfoBlob(read_info,(void *) NULL,0);
165  (void) FormatLocaleString(read_info->filename,MagickPathExtent,"group4:%s",
166    filename);
167  read_info->orientation=TopLeftOrientation;
168  image=ReadImage(read_info,exception);
169  if (image != (Image *) NULL)
170    {
171      (void) CopyMagickString(image->filename,image_info->filename,
172        MagickPathExtent);
173      (void) CopyMagickString(image->magick_filename,image_info->filename,
174        MagickPathExtent);
175      (void) CopyMagickString(image->magick,"G4",MagickPathExtent);
176    }
177  read_info=DestroyImageInfo(read_info);
178  (void) RelinquishUniqueFileResource(filename);
179  return(GetFirstImageInList(image));
180}
181
182static Image *ReadFAXImage(const ImageInfo *image_info,ExceptionInfo *exception)
183{
184  Image
185    *image;
186
187  MagickBooleanType
188    status;
189
190  /*
191    Open image file.
192  */
193  assert(image_info != (const ImageInfo *) NULL);
194  assert(image_info->signature == MagickCoreSignature);
195  if (image_info->debug != MagickFalse)
196    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
197      image_info->filename);
198  assert(exception != (ExceptionInfo *) NULL);
199  assert(exception->signature == MagickCoreSignature);
200  image=AcquireImage(image_info,exception);
201  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
202  if (status == MagickFalse)
203    {
204      image=DestroyImageList(image);
205      return((Image *) NULL);
206    }
207  /*
208    Initialize image structure.
209  */
210  image->storage_class=PseudoClass;
211  if (image->columns == 0)
212    image->columns=2592;
213  if (image->rows == 0)
214    image->rows=3508;
215  image->depth=8;
216  if (AcquireImageColormap(image,2,exception) == MagickFalse)
217    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
218  /*
219    Monochrome colormap.
220  */
221  image->colormap[0].red=QuantumRange;
222  image->colormap[0].green=QuantumRange;
223  image->colormap[0].blue=QuantumRange;
224  image->colormap[1].red=(Quantum) 0;
225  image->colormap[1].green=(Quantum) 0;
226  image->colormap[1].blue=(Quantum) 0;
227  if (image_info->ping != MagickFalse)
228    {
229      (void) CloseBlob(image);
230      return(GetFirstImageInList(image));
231    }
232  status=SetImageExtent(image,image->columns,image->rows,exception);
233  if (status == MagickFalse)
234    return(DestroyImageList(image));
235  if (LocaleCompare(image_info->magick,"G4") == 0)
236    return(FaxReadG4(image,image_info,exception));
237  else
238    return(FaxReadG3(image,exception));
239}
240
241/*
242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243%                                                                             %
244%                                                                             %
245%                                                                             %
246%   R e g i s t e r F A X I m a g e                                           %
247%                                                                             %
248%                                                                             %
249%                                                                             %
250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251%
252%  RegisterFAXImage() adds attributes for the FAX image format to
253%  the list of supported formats.  The attributes include the image format
254%  tag, a method to read and/or write the format, whether the format
255%  supports the saving of more than one frame to the same file or blob,
256%  whether the format supports native in-memory I/O, and a brief
257%  description of the format.
258%
259%  The format of the RegisterFAXImage method is:
260%
261%      size_t RegisterFAXImage(void)
262%
263*/
264ModuleExport size_t RegisterFAXImage(void)
265{
266  MagickInfo
267    *entry;
268
269  static const char
270    *Note=
271    {
272      "FAX machines use non-square pixels which are 1.5 times wider than\n"
273      "they are tall but computer displays use square pixels, therefore\n"
274      "FAX images may appear to be narrow unless they are explicitly\n"
275      "resized using a geometry of \"150x100%\".\n"
276    };
277
278  entry=AcquireMagickInfo("FAX","FAX","Group 3 FAX");
279  entry->decoder=(DecodeImageHandler *) ReadFAXImage;
280  entry->encoder=(EncodeImageHandler *) WriteFAXImage;
281  entry->magick=(IsImageFormatHandler *) IsFAX;
282  entry->note=ConstantString(Note);
283  (void) RegisterMagickInfo(entry);
284  entry=AcquireMagickInfo("FAX","G3","Group 3 FAX");
285  entry->decoder=(DecodeImageHandler *) ReadFAXImage;
286  entry->encoder=(EncodeImageHandler *) WriteFAXImage;
287  entry->magick=(IsImageFormatHandler *) IsFAX;
288  entry->flags^=CoderAdjoinFlag;
289  (void) RegisterMagickInfo(entry);
290  entry=AcquireMagickInfo("FAX","G4","Group 4 FAX");
291  entry->decoder=(DecodeImageHandler *) ReadFAXImage;
292  entry->encoder=(EncodeImageHandler *) WriteFAXImage;
293  entry->magick=(IsImageFormatHandler *) IsFAX;
294  entry->flags^=CoderAdjoinFlag;
295  (void) RegisterMagickInfo(entry);
296  return(MagickImageCoderSignature);
297}
298
299/*
300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
301%                                                                             %
302%                                                                             %
303%                                                                             %
304%   U n r e g i s t e r F A X I m a g e                                       %
305%                                                                             %
306%                                                                             %
307%                                                                             %
308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309%
310%  UnregisterFAXImage() removes format registrations made by the
311%  FAX module from the list of supported formats.
312%
313%  The format of the UnregisterFAXImage method is:
314%
315%      UnregisterFAXImage(void)
316%
317*/
318ModuleExport void UnregisterFAXImage(void)
319{
320  (void) UnregisterMagickInfo("FAX");
321  (void) UnregisterMagickInfo("G3");
322  (void) UnregisterMagickInfo("G4");
323}
324
325/*
326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327%                                                                             %
328%                                                                             %
329%                                                                             %
330%   W r i t e F A X I m a g e                                                 %
331%                                                                             %
332%                                                                             %
333%                                                                             %
334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335%
336%  WriteFAXImage() writes an image to a file in 1 dimensional Huffman encoded
337%  format.
338%
339%  The format of the WriteFAXImage method is:
340%
341%      MagickBooleanType WriteFAXImage(const ImageInfo *image_info,
342%        Image *image,ExceptionInfo *exception)
343%
344%  A description of each parameter follows.
345%
346%    o image_info: the image info.
347%
348%    o image:  The image.
349%
350%    o exception: return any errors or warnings in this structure.
351%
352*/
353static MagickBooleanType WriteFAXImage(const ImageInfo *image_info,Image *image,
354  ExceptionInfo *exception)
355{
356  ImageInfo
357    *write_info;
358
359  MagickBooleanType
360    status;
361
362  MagickOffsetType
363    scene;
364
365  /*
366    Open output image file.
367  */
368  assert(image_info != (const ImageInfo *) NULL);
369  assert(image_info->signature == MagickCoreSignature);
370  assert(image != (Image *) NULL);
371  assert(image->signature == MagickCoreSignature);
372  if (image->debug != MagickFalse)
373    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
374  assert(exception != (ExceptionInfo *) NULL);
375  assert(exception->signature == MagickCoreSignature);
376  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
377  if (status == MagickFalse)
378    return(status);
379  write_info=CloneImageInfo(image_info);
380  (void) CopyMagickString(write_info->magick,"FAX",MagickPathExtent);
381  scene=0;
382  do
383  {
384    /*
385      Convert MIFF to monochrome.
386    */
387    (void) TransformImageColorspace(image,sRGBColorspace,exception);
388    status=HuffmanEncodeImage(write_info,image,image,exception);
389    if (GetNextImageInList(image) == (Image *) NULL)
390      break;
391    image=SyncNextImageInList(image);
392    status=SetImageProgress(image,SaveImagesTag,scene++,
393      GetImageListLength(image));
394    if (status == MagickFalse)
395      break;
396  } while (write_info->adjoin != MagickFalse);
397  write_info=DestroyImageInfo(write_info);
398  (void) CloseBlob(image);
399  return(status);
400}
401