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 */
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) 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 */
RegisterCALSImage(void)310 ModuleExport 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 */
UnregisterCALSImage(void)358 ModuleExport 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
WriteCALSRecord(Image * image,const char * data)394 static 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
WriteCALSImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)425 static 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