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-2019 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 % https://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 */
74 static 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 */
IsCALS(const unsigned char * magick,const size_t length)103 static 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 */
ReadCALSImage(const ImageInfo * image_info,ExceptionInfo * exception)143 static 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) memset(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 if (fputc(c,file) != c)
261 break;
262 (void) fclose(file);
263 (void) CloseBlob(image);
264 image=DestroyImage(image);
265 read_info=CloneImageInfo(image_info);
266 SetImageInfoBlob(read_info,(void *) NULL,0);
267 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"group4:%s",
268 filename);
269 (void) FormatLocaleString(message,MagickPathExtent,"%lux%lu",width,height);
270 (void) CloneString(&read_info->size,message);
271 (void) FormatLocaleString(message,MagickPathExtent,"%lu",density);
272 (void) CloneString(&read_info->density,message);
273 read_info->orientation=(OrientationType) orientation;
274 image=ReadImage(read_info,exception);
275 if (image != (Image *) NULL)
276 {
277 (void) CopyMagickString(image->filename,image_info->filename,
278 MagickPathExtent);
279 (void) CopyMagickString(image->magick_filename,image_info->filename,
280 MagickPathExtent);
281 (void) CopyMagickString(image->magick,"CALS",MagickPathExtent);
282 }
283 read_info=DestroyImageInfo(read_info);
284 (void) RelinquishUniqueFileResource(filename);
285 return(image);
286 }
287
288 /*
289 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
290 % %
291 % %
292 % %
293 % R e g i s t e r C A L S I m a g e %
294 % %
295 % %
296 % %
297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
298 %
299 % RegisterCALSImage() adds attributes for the CALS Raster Group 1 image file
300 % image format to the list of supported formats. The attributes include the
301 % image format tag, a method to read and/or write the format, whether the
302 % format supports the saving of more than one frame to the same file or blob,
303 % whether the format supports native in-memory I/O, and a brief description
304 % of the format.
305 %
306 % The format of the RegisterCALSImage method is:
307 %
308 % size_t RegisterCALSImage(void)
309 %
310 */
RegisterCALSImage(void)311 ModuleExport size_t RegisterCALSImage(void)
312 {
313 #define CALSDescription "Continuous Acquisition and Life-cycle Support Type 1"
314 #define CALSNote "Specified in MIL-R-28002 and MIL-PRF-28002"
315
316 MagickInfo
317 *entry;
318
319 entry=AcquireMagickInfo("CALS","CAL",CALSDescription);
320 entry->decoder=(DecodeImageHandler *) ReadCALSImage;
321 #if defined(MAGICKCORE_TIFF_DELEGATE)
322 entry->encoder=(EncodeImageHandler *) WriteCALSImage;
323 #endif
324 entry->flags^=CoderAdjoinFlag;
325 entry->magick=(IsImageFormatHandler *) IsCALS;
326 entry->note=ConstantString(CALSNote);
327 (void) RegisterMagickInfo(entry);
328 entry=AcquireMagickInfo("CALS","CALS",CALSDescription);
329 entry->decoder=(DecodeImageHandler *) ReadCALSImage;
330 #if defined(MAGICKCORE_TIFF_DELEGATE)
331 entry->encoder=(EncodeImageHandler *) WriteCALSImage;
332 #endif
333 entry->flags^=CoderAdjoinFlag;
334 entry->magick=(IsImageFormatHandler *) IsCALS;
335 entry->note=ConstantString(CALSNote);
336 (void) RegisterMagickInfo(entry);
337 return(MagickImageCoderSignature);
338 }
339
340 /*
341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342 % %
343 % %
344 % %
345 % U n r e g i s t e r C A L S I m a g e %
346 % %
347 % %
348 % %
349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
350 %
351 % UnregisterCALSImage() removes format registrations made by the
352 % CALS module from the list of supported formats.
353 %
354 % The format of the UnregisterCALSImage method is:
355 %
356 % UnregisterCALSImage(void)
357 %
358 */
UnregisterCALSImage(void)359 ModuleExport void UnregisterCALSImage(void)
360 {
361 (void) UnregisterMagickInfo("CAL");
362 (void) UnregisterMagickInfo("CALS");
363 }
364
365 #if defined(MAGICKCORE_TIFF_DELEGATE)
366 /*
367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368 % %
369 % %
370 % %
371 % W r i t e C A L S I m a g e %
372 % %
373 % %
374 % %
375 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
376 %
377 % WriteCALSImage() writes an image to a file in CALS Raster Group 1 image
378 % format.
379 %
380 % The format of the WriteCALSImage method is:
381 %
382 % MagickBooleanType WriteCALSImage(const ImageInfo *image_info,
383 % Image *image,ExceptionInfo *exception)
384 %
385 % A description of each parameter follows.
386 %
387 % o image_info: the image info.
388 %
389 % o image: The image.
390 %
391 % o exception: return any errors or warnings in this structure.
392 %
393 */
394
WriteCALSRecord(Image * image,const char * data)395 static ssize_t WriteCALSRecord(Image *image,const char *data)
396 {
397 char
398 pad[128];
399
400 register const char
401 *p;
402
403 register ssize_t
404 i;
405
406 ssize_t
407 count;
408
409 i=0;
410 count=0;
411 if (data != (const char *) NULL)
412 {
413 p=data;
414 for (i=0; (i < 128) && (p[i] != '\0'); i++);
415 count=WriteBlob(image,(size_t) i,(const unsigned char *) data);
416 }
417 if (i < 128)
418 {
419 i=128-i;
420 (void) memset(pad,' ',(size_t) i);
421 count=WriteBlob(image,(size_t) i,(const unsigned char *) pad);
422 }
423 return(count);
424 }
425
WriteCALSImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)426 static MagickBooleanType WriteCALSImage(const ImageInfo *image_info,
427 Image *image,ExceptionInfo *exception)
428 {
429 char
430 header[129];
431
432 Image
433 *group4_image;
434
435 ImageInfo
436 *write_info;
437
438 MagickBooleanType
439 status;
440
441 register ssize_t
442 i;
443
444 size_t
445 density,
446 length,
447 orient_x,
448 orient_y;
449
450 ssize_t
451 count;
452
453 unsigned char
454 *group4;
455
456 /*
457 Open output image file.
458 */
459 assert(image_info != (const ImageInfo *) NULL);
460 assert(image_info->signature == MagickCoreSignature);
461 assert(image != (Image *) NULL);
462 assert(image->signature == MagickCoreSignature);
463 if (image->debug != MagickFalse)
464 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
465 assert(exception != (ExceptionInfo *) NULL);
466 assert(exception->signature == MagickCoreSignature);
467 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
468 if (status == MagickFalse)
469 return(status);
470 /*
471 Create standard CALS header.
472 */
473 count=WriteCALSRecord(image,"srcdocid: NONE");
474 (void) count;
475 count=WriteCALSRecord(image,"dstdocid: NONE");
476 count=WriteCALSRecord(image,"txtfilid: NONE");
477 count=WriteCALSRecord(image,"figid: NONE");
478 count=WriteCALSRecord(image,"srcgph: NONE");
479 count=WriteCALSRecord(image,"doccls: NONE");
480 count=WriteCALSRecord(image,"rtype: 1");
481 orient_x=0;
482 orient_y=0;
483 switch (image->orientation)
484 {
485 case TopRightOrientation:
486 {
487 orient_x=180;
488 orient_y=270;
489 break;
490 }
491 case BottomRightOrientation:
492 {
493 orient_x=180;
494 orient_y=90;
495 break;
496 }
497 case BottomLeftOrientation:
498 {
499 orient_y=90;
500 break;
501 }
502 case LeftTopOrientation:
503 {
504 orient_x=270;
505 break;
506 }
507 case RightTopOrientation:
508 {
509 orient_x=270;
510 orient_y=180;
511 break;
512 }
513 case RightBottomOrientation:
514 {
515 orient_x=90;
516 orient_y=180;
517 break;
518 }
519 case LeftBottomOrientation:
520 {
521 orient_x=90;
522 break;
523 }
524 default:
525 {
526 orient_y=270;
527 break;
528 }
529 }
530 (void) FormatLocaleString(header,sizeof(header),"rorient: %03ld,%03ld",
531 (long) orient_x,(long) orient_y);
532 count=WriteCALSRecord(image,header);
533 (void) FormatLocaleString(header,sizeof(header),"rpelcnt: %06lu,%06lu",
534 (unsigned long) image->columns,(unsigned long) image->rows);
535 count=WriteCALSRecord(image,header);
536 density=200;
537 if (image_info->density != (char *) NULL)
538 {
539 GeometryInfo
540 geometry_info;
541
542 (void) ParseGeometry(image_info->density,&geometry_info);
543 density=(size_t) floor(geometry_info.rho+0.5);
544 }
545 (void) FormatLocaleString(header,sizeof(header),"rdensty: %04lu",
546 (unsigned long) density);
547 count=WriteCALSRecord(image,header);
548 count=WriteCALSRecord(image,"notes: NONE");
549 (void) memset(header,' ',128);
550 for (i=0; i < 5; i++)
551 (void) WriteBlob(image,128,(unsigned char *) header);
552 /*
553 Write CALS pixels.
554 */
555 write_info=CloneImageInfo(image_info);
556 (void) CopyMagickString(write_info->filename,"GROUP4:",MagickPathExtent);
557 (void) CopyMagickString(write_info->magick,"GROUP4",MagickPathExtent);
558 group4_image=CloneImage(image,0,0,MagickTrue,exception);
559 if (group4_image == (Image *) NULL)
560 {
561 write_info=DestroyImageInfo(write_info);
562 (void) CloseBlob(image);
563 return(MagickFalse);
564 }
565 group4=(unsigned char *) ImageToBlob(write_info,group4_image,&length,
566 exception);
567 group4_image=DestroyImage(group4_image);
568 if (group4 == (unsigned char *) NULL)
569 {
570 write_info=DestroyImageInfo(write_info);
571 (void) CloseBlob(image);
572 return(MagickFalse);
573 }
574 write_info=DestroyImageInfo(write_info);
575 if (WriteBlob(image,length,group4) != (ssize_t) length)
576 status=MagickFalse;
577 group4=(unsigned char *) RelinquishMagickMemory(group4);
578 (void) CloseBlob(image);
579 return(status);
580 }
581 #endif
582