1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                         CCCC   AAA   L      SSSSS                           %
7%                        C      A   A  L      SS                              %
8%                        C      AAAAA  L       SSS                            %
9%                        C      A   A  L         SS                           %
10%                         CCCC  A   A  LLLLL  SSSSS                           %
11%                                                                             %
12%                                                                             %
13%                 Read/Write CALS Raster Group 1 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% The CALS raster format is a standard developed by the Computer Aided
37% Acquisition and Logistics Support (CALS) office of the United States
38% Department of Defense to standardize graphics data interchange for
39% electronic publishing, especially in the areas of technical graphics,
40% CAD/CAM, and image processing applications.
41%
42*/
43
44/*
45  Include declarations.
46*/
47#include "MagickCore/studio.h"
48#include "MagickCore/blob.h"
49#include "MagickCore/blob-private.h"
50#include "MagickCore/cache.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/constitute.h"
53#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/geometry.h"
56#include "MagickCore/image.h"
57#include "MagickCore/image-private.h"
58#include "MagickCore/list.h"
59#include "MagickCore/magick.h"
60#include "MagickCore/memory_.h"
61#include "MagickCore/monitor.h"
62#include "MagickCore/monitor-private.h"
63#include "MagickCore/option.h"
64#include "MagickCore/quantum-private.h"
65#include "MagickCore/resource_.h"
66#include "MagickCore/static.h"
67#include "MagickCore/string_.h"
68#include "MagickCore/module.h"
69
70#if defined(MAGICKCORE_TIFF_DELEGATE)
71/*
72 Forward declarations.
73*/
74static MagickBooleanType
75  WriteCALSImage(const ImageInfo *,Image *,ExceptionInfo *);
76#endif
77
78/*
79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80%                                                                             %
81%                                                                             %
82%                                                                             %
83%   I s C A L S                                                               %
84%                                                                             %
85%                                                                             %
86%                                                                             %
87%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88%
89%  IsCALS() returns MagickTrue if the image format type, identified by the
90%  magick string, is CALS Raster Group 1.
91%
92%  The format of the IsCALS method is:
93%
94%      MagickBooleanType IsCALS(const unsigned char *magick,const size_t length)
95%
96%  A description of each parameter follows:
97%
98%    o magick: compare image format pattern against these bytes.
99%
100%    o length: Specifies the length of the magick string.
101%
102*/
103static MagickBooleanType IsCALS(const unsigned char *magick,const size_t length)
104{
105  if (length < 128)
106    return(MagickFalse);
107  if (LocaleNCompare((const char *) magick,"version: MIL-STD-1840",21) == 0)
108    return(MagickTrue);
109  if (LocaleNCompare((const char *) magick,"srcdocid:",9) == 0)
110    return(MagickTrue);
111  if (LocaleNCompare((const char *) magick,"rorient:",8) == 0)
112    return(MagickTrue);
113  return(MagickFalse);
114}
115
116/*
117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118%                                                                             %
119%                                                                             %
120%                                                                             %
121%   R e a d C A L S I m a g e                                                 %
122%                                                                             %
123%                                                                             %
124%                                                                             %
125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126%
127%  ReadCALSImage() reads an CALS Raster Group 1 image format image file and
128%  returns it.  It allocates the memory necessary for the new Image structure
129%  and returns a pointer to the new image.
130%
131%  The format of the ReadCALSImage method is:
132%
133%      Image *ReadCALSImage(const ImageInfo *image_info,
134%        ExceptionInfo *exception)
135%
136%  A description of each parameter follows:
137%
138%    o image_info: the image info.
139%
140%    o exception: return any errors or warnings in this structure.
141%
142*/
143static Image *ReadCALSImage(const ImageInfo *image_info,
144  ExceptionInfo *exception)
145{
146  char
147    filename[MagickPathExtent],
148    header[MagickPathExtent],
149    message[MagickPathExtent];
150
151  FILE
152    *file;
153
154  Image
155    *image;
156
157  ImageInfo
158    *read_info;
159
160  int
161    c,
162    unique_file;
163
164  MagickBooleanType
165    status;
166
167  register ssize_t
168    i;
169
170  unsigned long
171    density,
172    direction,
173    height,
174    orientation,
175    pel_path,
176    type,
177    width;
178
179  /*
180    Open image file.
181  */
182  assert(image_info != (const ImageInfo *) NULL);
183  assert(image_info->signature == MagickCoreSignature);
184  if (image_info->debug != MagickFalse)
185    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
186      image_info->filename);
187  assert(exception != (ExceptionInfo *) NULL);
188  assert(exception->signature == MagickCoreSignature);
189  image=AcquireImage(image_info,exception);
190  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
191  if (status == MagickFalse)
192    {
193      image=DestroyImageList(image);
194      return((Image *) NULL);
195    }
196  /*
197    Read CALS header.
198  */
199  (void) ResetMagickMemory(header,0,sizeof(header));
200  density=0;
201  direction=0;
202  orientation=1;
203  pel_path=0;
204  type=1;
205  width=0;
206  height=0;
207  for (i=0; i < 16; i++)
208  {
209    if (ReadBlob(image,128,(unsigned char *) header) != 128)
210      break;
211    switch (*header)
212    {
213      case 'R':
214      case 'r':
215      {
216        if (LocaleNCompare(header,"rdensty:",8) == 0)
217          {
218            (void) sscanf(header+8,"%lu",&density);
219            break;
220          }
221        if (LocaleNCompare(header,"rpelcnt:",8) == 0)
222          {
223            (void) sscanf(header+8,"%lu,%lu",&width,&height);
224            break;
225          }
226        if (LocaleNCompare(header,"rorient:",8) == 0)
227          {
228            (void) sscanf(header+8,"%lu,%lu",&pel_path,&direction);
229            if (pel_path == 90)
230              orientation=5;
231            else
232              if (pel_path == 180)
233                orientation=3;
234              else
235                if (pel_path == 270)
236                  orientation=7;
237            if (direction == 90)
238              orientation++;
239            break;
240          }
241        if (LocaleNCompare(header,"rtype:",6) == 0)
242          {
243            (void) sscanf(header+6,"%lu",&type);
244            break;
245          }
246        break;
247      }
248    }
249  }
250  /*
251    Read CALS pixels.
252  */
253  file=(FILE *) NULL;
254  unique_file=AcquireUniqueFileResource(filename);
255  if (unique_file != -1)
256    file=fdopen(unique_file,"wb");
257  if ((unique_file == -1) || (file == (FILE *) NULL))
258    ThrowImageException(FileOpenError,"UnableToCreateTemporaryFile");
259  while ((c=ReadBlobByte(image)) != EOF)
260    (void) fputc(c,file);
261  (void) fclose(file);
262  (void) CloseBlob(image);
263  image=DestroyImage(image);
264  read_info=CloneImageInfo(image_info);
265  SetImageInfoBlob(read_info,(void *) NULL,0);
266  (void) FormatLocaleString(read_info->filename,MagickPathExtent,"group4:%s",
267    filename);
268  (void) FormatLocaleString(message,MagickPathExtent,"%lux%lu",width,height);
269  (void) CloneString(&read_info->size,message);
270  (void) FormatLocaleString(message,MagickPathExtent,"%lu",density);
271  (void) CloneString(&read_info->density,message);
272  read_info->orientation=(OrientationType) orientation;
273  image=ReadImage(read_info,exception);
274  if (image != (Image *) NULL)
275    {
276      (void) CopyMagickString(image->filename,image_info->filename,
277        MagickPathExtent);
278      (void) CopyMagickString(image->magick_filename,image_info->filename,
279        MagickPathExtent);
280      (void) CopyMagickString(image->magick,"CALS",MagickPathExtent);
281    }
282  read_info=DestroyImageInfo(read_info);
283  (void) RelinquishUniqueFileResource(filename);
284  return(image);
285}
286
287/*
288%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
289%                                                                             %
290%                                                                             %
291%                                                                             %
292%   R e g i s t e r C A L S I m a g e                                         %
293%                                                                             %
294%                                                                             %
295%                                                                             %
296%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
297%
298%  RegisterCALSImage() adds attributes for the CALS Raster Group 1 image file
299%  image format to the list of supported formats.  The attributes include the
300%  image format tag, a method to read and/or write the format, whether the
301%  format supports the saving of more than one frame to the same file or blob,
302%  whether the format supports native in-memory I/O, and a brief description
303%  of the format.
304%
305%  The format of the RegisterCALSImage method is:
306%
307%      size_t RegisterCALSImage(void)
308%
309*/
310ModuleExport size_t RegisterCALSImage(void)
311{
312#define CALSDescription  "Continuous Acquisition and Life-cycle Support Type 1"
313#define CALSNote  "Specified in MIL-R-28002 and MIL-PRF-28002"
314
315  MagickInfo
316    *entry;
317
318  entry=AcquireMagickInfo("CALS","CAL",CALSDescription);
319  entry->decoder=(DecodeImageHandler *) ReadCALSImage;
320#if defined(MAGICKCORE_TIFF_DELEGATE)
321  entry->encoder=(EncodeImageHandler *) WriteCALSImage;
322#endif
323  entry->flags^=CoderAdjoinFlag;
324  entry->magick=(IsImageFormatHandler *) IsCALS;
325  entry->note=ConstantString(CALSNote);
326  (void) RegisterMagickInfo(entry);
327  entry=AcquireMagickInfo("CALS","CALS",CALSDescription);
328  entry->decoder=(DecodeImageHandler *) ReadCALSImage;
329#if defined(MAGICKCORE_TIFF_DELEGATE)
330  entry->encoder=(EncodeImageHandler *) WriteCALSImage;
331#endif
332  entry->flags^=CoderAdjoinFlag;
333  entry->magick=(IsImageFormatHandler *) IsCALS;
334  entry->note=ConstantString(CALSNote);
335  (void) RegisterMagickInfo(entry);
336  return(MagickImageCoderSignature);
337}
338
339/*
340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341%                                                                             %
342%                                                                             %
343%                                                                             %
344%   U n r e g i s t e r C A L S I m a g e                                     %
345%                                                                             %
346%                                                                             %
347%                                                                             %
348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349%
350%  UnregisterCALSImage() removes format registrations made by the
351%  CALS module from the list of supported formats.
352%
353%  The format of the UnregisterCALSImage method is:
354%
355%      UnregisterCALSImage(void)
356%
357*/
358ModuleExport void UnregisterCALSImage(void)
359{
360  (void) UnregisterMagickInfo("CAL");
361  (void) UnregisterMagickInfo("CALS");
362}
363
364#if defined(MAGICKCORE_TIFF_DELEGATE)
365/*
366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
367%                                                                             %
368%                                                                             %
369%                                                                             %
370%   W r i t e C A L S I m a g e                                               %
371%                                                                             %
372%                                                                             %
373%                                                                             %
374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375%
376%  WriteCALSImage() writes an image to a file in CALS Raster Group 1 image
377%  format.
378%
379%  The format of the WriteCALSImage method is:
380%
381%      MagickBooleanType WriteCALSImage(const ImageInfo *image_info,
382%        Image *image,ExceptionInfo *exception)
383%
384%  A description of each parameter follows.
385%
386%    o image_info: the image info.
387%
388%    o image:  The image.
389%
390%    o exception: return any errors or warnings in this structure.
391%
392*/
393
394static ssize_t WriteCALSRecord(Image *image,const char *data)
395{
396  char
397    pad[128];
398
399  register const char
400    *p;
401
402  register ssize_t
403    i;
404
405  ssize_t
406    count;
407
408  i=0;
409  count=0;
410  if (data != (const char *) NULL)
411    {
412      p=data;
413      for (i=0; (i < 128) && (p[i] != '\0'); i++);
414      count=WriteBlob(image,(size_t) i,(const unsigned char *) data);
415    }
416  if (i < 128)
417    {
418      i=128-i;
419      (void) ResetMagickMemory(pad,' ',(size_t) i);
420      count=WriteBlob(image,(size_t) i,(const unsigned char *) pad);
421    }
422  return(count);
423}
424
425static MagickBooleanType WriteCALSImage(const ImageInfo *image_info,
426  Image *image,ExceptionInfo *exception)
427{
428  char
429    header[129];
430
431  Image
432    *group4_image;
433
434  ImageInfo
435    *write_info;
436
437  MagickBooleanType
438    status;
439
440  register ssize_t
441    i;
442
443  size_t
444    density,
445    length,
446    orient_x,
447    orient_y;
448
449  ssize_t
450    count;
451
452  unsigned char
453    *group4;
454
455  /*
456    Open output image file.
457  */
458  assert(image_info != (const ImageInfo *) NULL);
459  assert(image_info->signature == MagickCoreSignature);
460  assert(image != (Image *) NULL);
461  assert(image->signature == MagickCoreSignature);
462  if (image->debug != MagickFalse)
463    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
464  assert(exception != (ExceptionInfo *) NULL);
465  assert(exception->signature == MagickCoreSignature);
466  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
467  if (status == MagickFalse)
468    return(status);
469  /*
470    Create standard CALS header.
471  */
472  count=WriteCALSRecord(image,"srcdocid: NONE");
473  (void) count;
474  count=WriteCALSRecord(image,"dstdocid: NONE");
475  count=WriteCALSRecord(image,"txtfilid: NONE");
476  count=WriteCALSRecord(image,"figid: NONE");
477  count=WriteCALSRecord(image,"srcgph: NONE");
478  count=WriteCALSRecord(image,"doccls: NONE");
479  count=WriteCALSRecord(image,"rtype: 1");
480  orient_x=0;
481  orient_y=0;
482  switch (image->orientation)
483  {
484    case TopRightOrientation:
485    {
486      orient_x=180;
487      orient_y=270;
488      break;
489    }
490    case BottomRightOrientation:
491    {
492      orient_x=180;
493      orient_y=90;
494      break;
495    }
496    case BottomLeftOrientation:
497    {
498      orient_y=90;
499      break;
500    }
501    case LeftTopOrientation:
502    {
503      orient_x=270;
504      break;
505    }
506    case RightTopOrientation:
507    {
508      orient_x=270;
509      orient_y=180;
510      break;
511    }
512    case RightBottomOrientation:
513    {
514      orient_x=90;
515      orient_y=180;
516      break;
517    }
518    case LeftBottomOrientation:
519    {
520      orient_x=90;
521      break;
522    }
523    default:
524    {
525      orient_y=270;
526      break;
527    }
528  }
529  (void) FormatLocaleString(header,sizeof(header),"rorient: %03ld,%03ld",
530    (long) orient_x,(long) orient_y);
531  count=WriteCALSRecord(image,header);
532  (void) FormatLocaleString(header,sizeof(header),"rpelcnt: %06lu,%06lu",
533    (unsigned long) image->columns,(unsigned long) image->rows);
534  count=WriteCALSRecord(image,header);
535  density=200;
536  if (image_info->density != (char *) NULL)
537    {
538      GeometryInfo
539        geometry_info;
540
541      (void) ParseGeometry(image_info->density,&geometry_info);
542      density=(size_t) floor(geometry_info.rho+0.5);
543    }
544  (void) FormatLocaleString(header,sizeof(header),"rdensty: %04lu",
545    (unsigned long) density);
546  count=WriteCALSRecord(image,header);
547  count=WriteCALSRecord(image,"notes: NONE");
548  (void) ResetMagickMemory(header,' ',128);
549  for (i=0; i < 5; i++)
550    (void) WriteBlob(image,128,(unsigned char *) header);
551  /*
552    Write CALS pixels.
553  */
554  write_info=CloneImageInfo(image_info);
555  (void) CopyMagickString(write_info->filename,"GROUP4:",MagickPathExtent);
556  (void) CopyMagickString(write_info->magick,"GROUP4",MagickPathExtent);
557  group4_image=CloneImage(image,0,0,MagickTrue,exception);
558  if (group4_image == (Image *) NULL)
559    {
560      (void) CloseBlob(image);
561      return(MagickFalse);
562    }
563  group4=(unsigned char *) ImageToBlob(write_info,group4_image,&length,
564    exception);
565  group4_image=DestroyImage(group4_image);
566  if (group4 == (unsigned char *) NULL)
567    {
568      (void) CloseBlob(image);
569      return(MagickFalse);
570    }
571  write_info=DestroyImageInfo(write_info);
572  if (WriteBlob(image,length,group4) != (ssize_t) length)
573    status=MagickFalse;
574  group4=(unsigned char *) RelinquishMagickMemory(group4);
575  (void) CloseBlob(image);
576  return(status);
577}
578#endif
579