1 //-------------------------------------------------------------------------- 2 // Program to pull the information out of various types of EXIF digital 3 // camera files and show it in a reasonably consistent way 4 // 5 // This module handles basic Jpeg file handling 6 // 7 // Matthias Wandel 8 //-------------------------------------------------------------------------- 9 //#define LOG_NDEBUG 0 10 #define LOG_TAG "JHEAD" 11 #include <utils/Log.h> 12 #include "jhead.h" 13 14 // Storage for simplified info extracted from file. 15 ImageInfo_t ImageInfo; 16 17 18 static Section_t * Sections = NULL; 19 static int SectionsAllocated; 20 static int SectionsRead; 21 static int HaveAll; 22 23 // Define the line below to turn on poor man's debugging output 24 #undef SUPERDEBUG 25 26 #ifdef SUPERDEBUG 27 #define printf LOGE 28 #endif 29 30 31 32 #define PSEUDO_IMAGE_MARKER 0x123; // Extra value. 33 //-------------------------------------------------------------------------- 34 // Get 16 bits motorola order (always) for jpeg header stuff. 35 //-------------------------------------------------------------------------- 36 static int Get16m(const void * Short) 37 { 38 return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; 39 } 40 41 42 //-------------------------------------------------------------------------- 43 // Process a COM marker. 44 // We want to print out the marker contents as legible text; 45 // we must guard against random junk and varying newline representations. 46 //-------------------------------------------------------------------------- 47 static void process_COM (const uchar * Data, int length) 48 { 49 int ch; 50 char Comment[MAX_COMMENT_SIZE+1]; 51 int nch; 52 int a; 53 54 nch = 0; 55 56 if (length > MAX_COMMENT_SIZE) length = MAX_COMMENT_SIZE; // Truncate if it won't fit in our structure. 57 58 for (a=2;a<length;a++){ 59 ch = Data[a]; 60 61 if (ch == '\r' && Data[a+1] == '\n') continue; // Remove cr followed by lf. 62 63 if (ch >= 32 || ch == '\n' || ch == '\t'){ 64 Comment[nch++] = (char)ch; 65 }else{ 66 Comment[nch++] = '?'; 67 } 68 } 69 70 Comment[nch] = '\0'; // Null terminate 71 72 if (ShowTags){ 73 printf("COM marker comment: %s\n",Comment); 74 } 75 76 strcpy(ImageInfo.Comments,Comment); 77 ImageInfo.CommentWidchars = 0; 78 } 79 80 81 //-------------------------------------------------------------------------- 82 // Process a SOFn marker. This is useful for the image dimensions 83 //-------------------------------------------------------------------------- 84 static void process_SOFn (const uchar * Data, int marker) 85 { 86 int data_precision, num_components; 87 88 data_precision = Data[2]; 89 ImageInfo.Height = Get16m(Data+3); 90 ImageInfo.Width = Get16m(Data+5); 91 num_components = Data[7]; 92 93 if (num_components == 3){ 94 ImageInfo.IsColor = 1; 95 }else{ 96 ImageInfo.IsColor = 0; 97 } 98 99 ImageInfo.Process = marker; 100 101 if (ShowTags){ 102 printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n", 103 ImageInfo.Width, ImageInfo.Height, num_components, data_precision); 104 } 105 } 106 107 108 //-------------------------------------------------------------------------- 109 // Check sections array to see if it needs to be increased in size. 110 //-------------------------------------------------------------------------- 111 void CheckSectionsAllocated(void) 112 { 113 if (SectionsRead > SectionsAllocated){ 114 ErrFatal("allocation screwup"); 115 } 116 if (SectionsRead >= SectionsAllocated){ 117 SectionsAllocated += SectionsAllocated/2; 118 Sections = (Section_t *)realloc(Sections, sizeof(Section_t)*SectionsAllocated); 119 if (Sections == NULL){ 120 ErrFatal("could not allocate data for entire image"); 121 } 122 } 123 } 124 125 126 //-------------------------------------------------------------------------- 127 // Parse the marker stream until SOS or EOI is seen; 128 //-------------------------------------------------------------------------- 129 int ReadJpegSections (FILE * infile, ReadMode_t ReadMode) 130 { 131 int a; 132 int HaveCom = FALSE; 133 134 a = fgetc(infile); 135 136 if (a != 0xff || fgetc(infile) != M_SOI){ 137 return FALSE; 138 } 139 for(;;){ 140 int itemlen; 141 int marker = 0; 142 int ll,lh, got; 143 uchar * Data; 144 145 CheckSectionsAllocated(); 146 147 for (a=0;a<=16;a++){ 148 marker = fgetc(infile); 149 if (marker != 0xff) break; 150 151 if (a >= 16){ 152 fprintf(stderr,"too many padding bytes\n"); 153 return FALSE; 154 } 155 } 156 157 158 Sections[SectionsRead].Type = marker; 159 160 // Read the length of the section. 161 lh = fgetc(infile); 162 ll = fgetc(infile); 163 164 itemlen = (lh << 8) | ll; 165 166 if (itemlen < 2){ 167 // ErrFatal("invalid marker"); 168 LOGE("invalid marker"); 169 return FALSE; 170 } 171 172 Sections[SectionsRead].Size = itemlen; 173 174 Data = (uchar *)malloc(itemlen); 175 if (Data == NULL){ 176 // ErrFatal("Could not allocate memory"); 177 LOGE("Could not allocate memory"); 178 return 0; 179 } 180 Sections[SectionsRead].Data = Data; 181 182 // Store first two pre-read bytes. 183 Data[0] = (uchar)lh; 184 Data[1] = (uchar)ll; 185 186 got = fread(Data+2, 1, itemlen-2, infile); // Read the whole section. 187 if (got != itemlen-2){ 188 // ErrFatal("Premature end of file?"); 189 LOGE("Premature end of file?"); 190 return FALSE; 191 } 192 SectionsRead += 1; 193 194 printf("reading marker %d", marker); 195 switch(marker){ 196 197 case M_SOS: // stop before hitting compressed data 198 // If reading entire image is requested, read the rest of the data. 199 if (ReadMode & READ_IMAGE){ 200 int cp, ep, size; 201 // Determine how much file is left. 202 cp = ftell(infile); 203 fseek(infile, 0, SEEK_END); 204 ep = ftell(infile); 205 fseek(infile, cp, SEEK_SET); 206 207 size = ep-cp; 208 Data = (uchar *)malloc(size); 209 if (Data == NULL){ 210 // ErrFatal("could not allocate data for entire image"); 211 LOGE("could not allocate data for entire image"); 212 return FALSE; 213 } 214 215 got = fread(Data, 1, size, infile); 216 if (got != size){ 217 // ErrFatal("could not read the rest of the image"); 218 LOGE("could not read the rest of the image"); 219 return FALSE; 220 } 221 222 CheckSectionsAllocated(); 223 Sections[SectionsRead].Data = Data; 224 Sections[SectionsRead].Size = size; 225 Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; 226 SectionsRead ++; 227 HaveAll = 1; 228 } 229 return TRUE; 230 231 case M_EOI: // in case it's a tables-only JPEG stream 232 fprintf(stderr,"No image in jpeg!\n"); 233 return FALSE; 234 235 case M_COM: // Comment section 236 if (HaveCom || ((ReadMode & READ_METADATA) == 0)){ 237 // Discard this section. 238 free(Sections[--SectionsRead].Data); 239 }else{ 240 process_COM(Data, itemlen); 241 HaveCom = TRUE; 242 } 243 break; 244 245 case M_JFIF: 246 // Regular jpegs always have this tag, exif images have the exif 247 // marker instead, althogh ACDsee will write images with both markers. 248 // this program will re-create this marker on absence of exif marker. 249 // hence no need to keep the copy from the file. 250 free(Sections[--SectionsRead].Data); 251 break; 252 253 case M_EXIF: 254 // There can be different section using the same marker. 255 if (ReadMode & READ_METADATA){ 256 if (memcmp(Data+2, "Exif", 4) == 0){ 257 process_EXIF(Data, itemlen); 258 break; 259 }else if (memcmp(Data+2, "http:", 5) == 0){ 260 Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes. 261 if (ShowTags){ 262 printf("Image cotains XMP section, %d bytes long\n", itemlen); 263 if (ShowTags){ 264 ShowXmp(Sections[SectionsRead-1]); 265 } 266 } 267 break; 268 } 269 } 270 // Oterwise, discard this section. 271 free(Sections[--SectionsRead].Data); 272 break; 273 274 case M_IPTC: 275 if (ReadMode & READ_METADATA){ 276 if (ShowTags){ 277 printf("Image cotains IPTC section, %d bytes long\n", itemlen); 278 } 279 // Note: We just store the IPTC section. Its relatively straightforward 280 // and we don't act on any part of it, so just display it at parse time. 281 }else{ 282 free(Sections[--SectionsRead].Data); 283 } 284 break; 285 286 case M_SOF0: 287 case M_SOF1: 288 case M_SOF2: 289 case M_SOF3: 290 case M_SOF5: 291 case M_SOF6: 292 case M_SOF7: 293 case M_SOF9: 294 case M_SOF10: 295 case M_SOF11: 296 case M_SOF13: 297 case M_SOF14: 298 case M_SOF15: 299 process_SOFn(Data, marker); 300 break; 301 default: 302 // Skip any other sections. 303 if (ShowTags){ 304 printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen); 305 } 306 break; 307 } 308 } 309 return TRUE; 310 } 311 312 //-------------------------------------------------------------------------- 313 // Parse the marker buffer until SOS or EOI is seen; 314 //-------------------------------------------------------------------------- 315 int ReadJpegSectionsFromBuffer (unsigned char* buffer, unsigned int buffer_size, ReadMode_t ReadMode) 316 { 317 int a; 318 unsigned int pos = 0; 319 int HaveCom = FALSE; 320 321 if (!buffer) { 322 return FALSE; 323 } 324 325 if (buffer_size < 1) { 326 return FALSE; 327 } 328 329 a = (int) buffer[pos++]; 330 331 if (a != 0xff || buffer[pos++] != M_SOI){ 332 return FALSE; 333 } 334 335 for(;;){ 336 int itemlen; 337 int marker = 0; 338 int ll,lh, got; 339 uchar * Data; 340 341 CheckSectionsAllocated(); 342 343 for (a=0;a<=16;a++){ 344 marker = buffer[pos++]; 345 if (marker != 0xff) break; 346 347 if (a >= 16){ 348 fprintf(stderr,"too many padding bytes\n"); 349 return FALSE; 350 } 351 } 352 353 Sections[SectionsRead].Type = marker; 354 355 // Read the length of the section. 356 lh = buffer[pos++]; 357 ll = buffer[pos++]; 358 359 itemlen = (lh << 8) | ll; 360 361 if (itemlen < 2) { 362 LOGE("invalid marker"); 363 return FALSE; 364 } 365 366 Sections[SectionsRead].Size = itemlen; 367 368 Data = (uchar *)malloc(itemlen); 369 if (Data == NULL) { 370 LOGE("Could not allocate memory"); 371 return 0; 372 } 373 Sections[SectionsRead].Data = Data; 374 375 // Store first two pre-read bytes. 376 Data[0] = (uchar)lh; 377 Data[1] = (uchar)ll; 378 379 if (pos+itemlen-2 > buffer_size) { 380 LOGE("Premature end of file?"); 381 return FALSE; 382 } 383 384 memcpy(Data+2, buffer+pos, itemlen-2); // Read the whole section. 385 pos += itemlen-2; 386 387 SectionsRead += 1; 388 389 printf("reading marker %d", marker); 390 switch(marker){ 391 392 case M_SOS: // stop before hitting compressed data 393 // If reading entire image is requested, read the rest of the data. 394 if (ReadMode & READ_IMAGE){ 395 int size; 396 // Determine how much file is left. 397 size = buffer_size - pos; 398 399 if (size < 1) { 400 LOGE("could not read the rest of the image"); 401 return FALSE; 402 } 403 Data = (uchar *)malloc(size); 404 if (Data == NULL) { 405 LOGE("%d: could not allocate data for entire image size: %d", __LINE__, size); 406 return FALSE; 407 } 408 409 memcpy(Data, buffer+pos, size); 410 411 CheckSectionsAllocated(); 412 Sections[SectionsRead].Data = Data; 413 Sections[SectionsRead].Size = size; 414 Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; 415 SectionsRead ++; 416 HaveAll = 1; 417 } 418 return TRUE; 419 420 case M_EOI: // in case it's a tables-only JPEG stream 421 LOGE("No image in jpeg!\n"); 422 return FALSE; 423 424 case M_COM: // Comment section 425 if (HaveCom || ((ReadMode & READ_METADATA) == 0)){ 426 // Discard this section. 427 free(Sections[--SectionsRead].Data); 428 }else{ 429 process_COM(Data, itemlen); 430 HaveCom = TRUE; 431 } 432 break; 433 434 case M_JFIF: 435 // Regular jpegs always have this tag, exif images have the exif 436 // marker instead, althogh ACDsee will write images with both markers. 437 // this program will re-create this marker on absence of exif marker. 438 // hence no need to keep the copy from the file. 439 free(Sections[--SectionsRead].Data); 440 break; 441 442 case M_EXIF: 443 // There can be different section using the same marker. 444 if (ReadMode & READ_METADATA){ 445 if (memcmp(Data+2, "Exif", 4) == 0){ 446 process_EXIF(Data, itemlen); 447 break; 448 }else if (memcmp(Data+2, "http:", 5) == 0){ 449 Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes. 450 if (ShowTags){ 451 LOGD("Image cotains XMP section, %d bytes long\n", itemlen); 452 if (ShowTags){ 453 ShowXmp(Sections[SectionsRead-1]); 454 } 455 } 456 break; 457 } 458 } 459 // Oterwise, discard this section. 460 free(Sections[--SectionsRead].Data); 461 break; 462 463 case M_IPTC: 464 if (ReadMode & READ_METADATA){ 465 if (ShowTags){ 466 LOGD("Image cotains IPTC section, %d bytes long\n", itemlen); 467 } 468 // Note: We just store the IPTC section. Its relatively straightforward 469 // and we don't act on any part of it, so just display it at parse time. 470 }else{ 471 free(Sections[--SectionsRead].Data); 472 } 473 break; 474 475 case M_SOF0: 476 case M_SOF1: 477 case M_SOF2: 478 case M_SOF3: 479 case M_SOF5: 480 case M_SOF6: 481 case M_SOF7: 482 case M_SOF9: 483 case M_SOF10: 484 case M_SOF11: 485 case M_SOF13: 486 case M_SOF14: 487 case M_SOF15: 488 process_SOFn(Data, marker); 489 break; 490 default: 491 // Skip any other sections. 492 if (ShowTags){ 493 LOGD("Jpeg section marker 0x%02x size %d\n",marker, itemlen); 494 } 495 break; 496 } 497 } 498 return TRUE; 499 } 500 501 //-------------------------------------------------------------------------- 502 // Discard read data. 503 //-------------------------------------------------------------------------- 504 void DiscardData(void) 505 { 506 int a; 507 508 for (a=0;a<SectionsRead;a++){ 509 free(Sections[a].Data); 510 } 511 512 memset(&ImageInfo, 0, sizeof(ImageInfo)); 513 SectionsRead = 0; 514 HaveAll = 0; 515 } 516 517 //-------------------------------------------------------------------------- 518 // Read image data. 519 //-------------------------------------------------------------------------- 520 int ReadJpegFile(const char * FileName, ReadMode_t ReadMode) 521 { 522 FILE * infile; 523 int ret; 524 525 infile = fopen(FileName, "rb"); // Unix ignores 'b', windows needs it. 526 527 if (infile == NULL) { 528 LOGE("can't open '%s'", FileName); 529 fprintf(stderr, "can't open '%s'\n", FileName); 530 return FALSE; 531 } 532 533 // Scan the JPEG headers. 534 printf("ReadJpegSections"); 535 ret = ReadJpegSections(infile, ReadMode); 536 if (!ret){ 537 LOGV("Cannot parse JPEG sections for file: %s", FileName); 538 fprintf(stderr,"Not JPEG: %s\n",FileName); 539 } 540 541 fclose(infile); 542 543 if (ret == FALSE){ 544 DiscardData(); 545 } 546 return ret; 547 } 548 549 550 //-------------------------------------------------------------------------- 551 // Replace or remove exif thumbnail 552 //-------------------------------------------------------------------------- 553 int SaveThumbnail(char * ThumbFileName) 554 { 555 FILE * ThumbnailFile; 556 557 if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailSize == 0){ 558 fprintf(stderr,"Image contains no thumbnail\n"); 559 return FALSE; 560 } 561 562 if (strcmp(ThumbFileName, "-") == 0){ 563 // A filename of '-' indicates thumbnail goes to stdout. 564 // This doesn't make much sense under Windows, so this feature is unix only. 565 ThumbnailFile = stdout; 566 }else{ 567 ThumbnailFile = fopen(ThumbFileName,"wb"); 568 } 569 570 if (ThumbnailFile){ 571 uchar * ThumbnailPointer; 572 Section_t * ExifSection; 573 ExifSection = FindSection(M_EXIF); 574 ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8; 575 576 fwrite(ThumbnailPointer, ImageInfo.ThumbnailSize ,1, ThumbnailFile); 577 fclose(ThumbnailFile); 578 return TRUE; 579 }else{ 580 // ErrFatal("Could not write thumbnail file"); 581 LOGE("Could not write thumbnail file"); 582 return FALSE; 583 } 584 } 585 586 //-------------------------------------------------------------------------- 587 // Replace or remove exif thumbnail 588 //-------------------------------------------------------------------------- 589 int ReplaceThumbnailFromBuffer(const char * Thumb, int ThumbLen) 590 { 591 int NewExifSize; 592 Section_t * ExifSection; 593 uchar * ThumbnailPointer; 594 595 if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE){ 596 if (Thumb == NULL){ 597 // Delete of nonexistent thumbnail (not even pointers present) 598 // No action, no error. 599 return FALSE; 600 } 601 602 // Adding or removing of thumbnail is not possible - that would require rearranging 603 // of the exif header, which is risky, and jhad doesn't know how to do. 604 fprintf(stderr,"Image contains no thumbnail to replace - add is not possible\n"); 605 #ifdef SUPERDEBUG 606 LOGE("Image contains no thumbnail to replace - add is not possible\n"); 607 #endif 608 return FALSE; 609 } 610 611 if (Thumb) { 612 if (ThumbLen + ImageInfo.ThumbnailOffset > 0x10000-20){ 613 //ErrFatal("Thumbnail is too large to insert into exif header"); 614 LOGE("Thumbnail is too large to insert into exif header"); 615 return FALSE; 616 } 617 } else { 618 if (ImageInfo.ThumbnailSize == 0){ 619 return FALSE; 620 } 621 622 ThumbLen = 0; 623 } 624 625 ExifSection = FindSection(M_EXIF); 626 627 NewExifSize = ImageInfo.ThumbnailOffset+8+ThumbLen; 628 ExifSection->Data = (uchar *)realloc(ExifSection->Data, NewExifSize); 629 630 ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8; 631 632 if (Thumb){ 633 memcpy(ThumbnailPointer, Thumb, ThumbLen); 634 } 635 636 ImageInfo.ThumbnailSize = ThumbLen; 637 638 Put32u(ExifSection->Data+ImageInfo.ThumbnailSizeOffset+8, ThumbLen); 639 640 ExifSection->Data[0] = (uchar)(NewExifSize >> 8); 641 ExifSection->Data[1] = (uchar)NewExifSize; 642 ExifSection->Size = NewExifSize; 643 644 #ifdef SUPERDEBUG 645 LOGE("ReplaceThumbnail successful thumblen %d", ThumbLen); 646 #endif 647 return TRUE; 648 } 649 650 //-------------------------------------------------------------------------- 651 // Replace or remove exif thumbnail 652 //-------------------------------------------------------------------------- 653 int ReplaceThumbnail(const char * ThumbFileName) 654 { 655 FILE * ThumbnailFile; 656 int ThumbLen, NewExifSize; 657 Section_t * ExifSection; 658 uchar * ThumbnailPointer; 659 660 if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE){ 661 if (ThumbFileName == NULL){ 662 // Delete of nonexistent thumbnail (not even pointers present) 663 // No action, no error. 664 return FALSE; 665 } 666 667 // Adding or removing of thumbnail is not possible - that would require rearranging 668 // of the exif header, which is risky, and jhad doesn't know how to do. 669 fprintf(stderr,"Image contains no thumbnail to replace - add is not possible\n"); 670 #ifdef SUPERDEBUG 671 LOGE("Image contains no thumbnail to replace - add is not possible\n"); 672 #endif 673 return FALSE; 674 } 675 676 if (ThumbFileName){ 677 ThumbnailFile = fopen(ThumbFileName,"rb"); 678 679 if (ThumbnailFile == NULL){ 680 //ErrFatal("Could not read thumbnail file"); 681 LOGE("Could not read thumbnail file"); 682 return FALSE; 683 } 684 685 // get length 686 fseek(ThumbnailFile, 0, SEEK_END); 687 688 ThumbLen = ftell(ThumbnailFile); 689 fseek(ThumbnailFile, 0, SEEK_SET); 690 691 if (ThumbLen + ImageInfo.ThumbnailOffset > 0x10000-20){ 692 //ErrFatal("Thumbnail is too large to insert into exif header"); 693 LOGE("Thumbnail is too large to insert into exif header"); 694 return FALSE; 695 } 696 }else{ 697 if (ImageInfo.ThumbnailSize == 0){ 698 return FALSE; 699 } 700 701 ThumbLen = 0; 702 ThumbnailFile = NULL; 703 } 704 705 ExifSection = FindSection(M_EXIF); 706 707 NewExifSize = ImageInfo.ThumbnailOffset+8+ThumbLen; 708 ExifSection->Data = (uchar *)realloc(ExifSection->Data, NewExifSize); 709 710 ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8; 711 712 if (ThumbnailFile){ 713 fread(ThumbnailPointer, ThumbLen, 1, ThumbnailFile); 714 fclose(ThumbnailFile); 715 } 716 717 ImageInfo.ThumbnailSize = ThumbLen; 718 719 Put32u(ExifSection->Data+ImageInfo.ThumbnailSizeOffset+8, ThumbLen); 720 721 ExifSection->Data[0] = (uchar)(NewExifSize >> 8); 722 ExifSection->Data[1] = (uchar)NewExifSize; 723 ExifSection->Size = NewExifSize; 724 725 #ifdef SUPERDEBUG 726 LOGE("ReplaceThumbnail successful thumblen %d", ThumbLen); 727 #endif 728 return TRUE; 729 } 730 731 732 //-------------------------------------------------------------------------- 733 // Discard everything but the exif and comment sections. 734 //-------------------------------------------------------------------------- 735 void DiscardAllButExif(void) 736 { 737 Section_t ExifKeeper; 738 Section_t CommentKeeper; 739 Section_t IptcKeeper; 740 Section_t XmpKeeper; 741 int a; 742 743 memset(&ExifKeeper, 0, sizeof(ExifKeeper)); 744 memset(&CommentKeeper, 0, sizeof(CommentKeeper)); 745 memset(&IptcKeeper, 0, sizeof(IptcKeeper)); 746 memset(&XmpKeeper, 0, sizeof(IptcKeeper)); 747 748 for (a=0;a<SectionsRead;a++){ 749 if (Sections[a].Type == M_EXIF && ExifKeeper.Type == 0){ 750 ExifKeeper = Sections[a]; 751 }else if (Sections[a].Type == M_XMP && XmpKeeper.Type == 0){ 752 XmpKeeper = Sections[a]; 753 }else if (Sections[a].Type == M_COM && CommentKeeper.Type == 0){ 754 CommentKeeper = Sections[a]; 755 }else if (Sections[a].Type == M_IPTC && IptcKeeper.Type == 0){ 756 IptcKeeper = Sections[a]; 757 }else{ 758 free(Sections[a].Data); 759 } 760 } 761 SectionsRead = 0; 762 if (ExifKeeper.Type){ 763 CheckSectionsAllocated(); 764 Sections[SectionsRead++] = ExifKeeper; 765 } 766 if (CommentKeeper.Type){ 767 CheckSectionsAllocated(); 768 Sections[SectionsRead++] = CommentKeeper; 769 } 770 if (IptcKeeper.Type){ 771 CheckSectionsAllocated(); 772 Sections[SectionsRead++] = IptcKeeper; 773 } 774 775 if (XmpKeeper.Type){ 776 CheckSectionsAllocated(); 777 Sections[SectionsRead++] = XmpKeeper; 778 } 779 } 780 781 //-------------------------------------------------------------------------- 782 // Write image data back to disk. 783 //-------------------------------------------------------------------------- 784 int WriteJpegFile(const char * FileName) 785 { 786 FILE * outfile; 787 int a; 788 789 if (!HaveAll){ 790 LOGE("Can't write back - didn't read all"); 791 return FALSE; 792 } 793 794 outfile = fopen(FileName,"wb"); 795 if (outfile == NULL){ 796 LOGE("Could not open file for write"); 797 return FALSE; 798 } 799 800 // Initial static jpeg marker. 801 fputc(0xff,outfile); 802 fputc(0xd8,outfile); 803 804 if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){ 805 // The image must start with an exif or jfif marker. If we threw those away, create one. 806 static uchar JfifHead[18] = { 807 0xff, M_JFIF, 808 0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01, 809 0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00 810 }; 811 fwrite(JfifHead, 18, 1, outfile); 812 } 813 814 int writeOk = FALSE; 815 int nWrite = 0; 816 // Write all the misc sections 817 for (a=0;a<SectionsRead-1;a++){ 818 fputc(0xff,outfile); 819 fputc((unsigned char)Sections[a].Type, outfile); 820 nWrite = fwrite(Sections[a].Data, 1, Sections[a].Size, outfile); 821 writeOk = (nWrite == Sections[a].Size); 822 if(!writeOk){ 823 LOGE("write section %d failed expect %d actual %d",a,Sections[a].Size,nWrite); 824 break; 825 } 826 } 827 828 // Write the remaining image data. 829 if (writeOk){ 830 nWrite = fwrite(Sections[a].Data, 1,Sections[a].Size, outfile); 831 writeOk = (nWrite == Sections[a].Size); 832 if (!writeOk){ 833 LOGE("write section %d failed expect %d actual %d",a,Sections[a].Size,nWrite); 834 } 835 } 836 837 fclose(outfile); 838 return writeOk; 839 } 840 841 //-------------------------------------------------------------------------- 842 // Write image to a buffer 843 //-------------------------------------------------------------------------- 844 int WriteJpegToBuffer(unsigned char* buffer, unsigned int buffer_size) 845 { 846 unsigned int pos = 0; 847 int a; 848 849 if (!buffer) { 850 return FALSE; 851 } 852 853 if (buffer_size < 1) { 854 return FALSE; 855 } 856 857 if (!HaveAll){ 858 LOGE("Can't write back - didn't read all"); 859 return FALSE; 860 } 861 862 // Initial static jpeg marker. 863 buffer[pos++] = 0xff; 864 buffer[pos++] = 0xd8; 865 866 if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){ 867 // The image must start with an exif or jfif marker. If we threw those away, create one. 868 static uchar JfifHead[18] = { 869 0xff, M_JFIF, 870 0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01, 871 0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00 872 }; 873 memcpy(buffer+pos, JfifHead, 18); 874 pos+= 18; 875 } 876 877 int writeOk = FALSE; 878 int nWrite = 0; 879 // Write all the misc sections 880 for (a=0;a<SectionsRead-1;a++){ 881 buffer[pos++] = 0xff; 882 buffer[pos++] = (unsigned char) Sections[a].Type; 883 if (pos+Sections[a].Size > buffer_size) { 884 writeOk = FALSE; 885 break; 886 } 887 memcpy(buffer+pos, Sections[a].Data, Sections[a].Size); 888 pos += Sections[a].Size; 889 writeOk = TRUE; 890 } 891 892 // Write the remaining image data. 893 if (writeOk){ 894 if (pos+Sections[a].Size > buffer_size) { 895 writeOk = FALSE; 896 } else { 897 memcpy(buffer+pos, Sections[a].Data, Sections[a].Size); 898 pos += Sections[a].Size; 899 writeOk = TRUE; 900 } 901 } 902 return writeOk; 903 } 904 905 906 //-------------------------------------------------------------------------- 907 // Check if image has exif header. 908 //-------------------------------------------------------------------------- 909 Section_t * FindSection(int SectionType) 910 { 911 int a; 912 913 for (a=0;a<SectionsRead;a++){ 914 if (Sections[a].Type == SectionType){ 915 return &Sections[a]; 916 } 917 } 918 // Could not be found. 919 return NULL; 920 } 921 922 //-------------------------------------------------------------------------- 923 // Remove a certain type of section. 924 //-------------------------------------------------------------------------- 925 int RemoveSectionType(int SectionType) 926 { 927 int a; 928 for (a=0;a<SectionsRead-1;a++){ 929 if (Sections[a].Type == SectionType){ 930 // Free up this section 931 free (Sections[a].Data); 932 // Move succeding sections back by one to close space in array. 933 memmove(Sections+a, Sections+a+1, sizeof(Section_t) * (SectionsRead-a)); 934 SectionsRead -= 1; 935 return TRUE; 936 } 937 } 938 return FALSE; 939 } 940 941 //-------------------------------------------------------------------------- 942 // Remove sectons not part of image and not exif or comment sections. 943 //-------------------------------------------------------------------------- 944 int RemoveUnknownSections(void) 945 { 946 int a; 947 int Modified = FALSE; 948 for (a=0;a<SectionsRead-1;){ 949 switch(Sections[a].Type){ 950 case M_SOF0: 951 case M_SOF1: 952 case M_SOF2: 953 case M_SOF3: 954 case M_SOF5: 955 case M_SOF6: 956 case M_SOF7: 957 case M_SOF9: 958 case M_SOF10: 959 case M_SOF11: 960 case M_SOF13: 961 case M_SOF14: 962 case M_SOF15: 963 case M_SOI: 964 case M_EOI: 965 case M_SOS: 966 case M_JFIF: 967 case M_EXIF: 968 case M_XMP: 969 case M_COM: 970 case M_DQT: 971 case M_DHT: 972 case M_DRI: 973 case M_IPTC: 974 // keep. 975 a++; 976 break; 977 default: 978 // Unknown. Delete. 979 free (Sections[a].Data); 980 // Move succeding sections back by one to close space in array. 981 memmove(Sections+a, Sections+a+1, sizeof(Section_t) * (SectionsRead-a)); 982 SectionsRead -= 1; 983 Modified = TRUE; 984 } 985 } 986 return Modified; 987 } 988 989 //-------------------------------------------------------------------------- 990 // Add a section (assume it doesn't already exist) - used for 991 // adding comment sections and exif sections 992 //-------------------------------------------------------------------------- 993 Section_t * CreateSection(int SectionType, unsigned char * Data, int Size) 994 { 995 Section_t * NewSection; 996 int a; 997 int NewIndex; 998 NewIndex = 2; 999 1000 if (SectionType == M_EXIF) NewIndex = 0; // Exif alwas goes first! 1001 1002 // Insert it in third position - seems like a safe place to put 1003 // things like comments. 1004 1005 if (SectionsRead < NewIndex){ 1006 // ErrFatal("Too few sections!"); 1007 LOGE("Too few sections!"); 1008 return FALSE; 1009 } 1010 1011 CheckSectionsAllocated(); 1012 for (a=SectionsRead;a>NewIndex;a--){ 1013 Sections[a] = Sections[a-1]; 1014 } 1015 SectionsRead += 1; 1016 1017 NewSection = Sections+NewIndex; 1018 1019 NewSection->Type = SectionType; 1020 NewSection->Size = Size; 1021 NewSection->Data = Data; 1022 1023 return NewSection; 1024 } 1025 1026 1027 //-------------------------------------------------------------------------- 1028 // Initialisation. 1029 //-------------------------------------------------------------------------- 1030 void ResetJpgfile(void) 1031 { 1032 if (Sections == NULL){ 1033 Sections = (Section_t *)malloc(sizeof(Section_t)*5); 1034 SectionsAllocated = 5; 1035 } 1036 1037 SectionsRead = 0; 1038 HaveAll = 0; 1039 } 1040