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