Home | History | Annotate | Download | only in jhead
      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