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