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