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.
15ImageInfo_t ImageInfo;
16
17
18static Section_t * Sections = NULL;
19static int SectionsAllocated;
20static int SectionsRead;
21static 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//--------------------------------------------------------------------------
36static 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//--------------------------------------------------------------------------
47static 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//--------------------------------------------------------------------------
84static 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//--------------------------------------------------------------------------
111void 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//--------------------------------------------------------------------------
129int 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//--------------------------------------------------------------------------
317int 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//--------------------------------------------------------------------------
508void 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//--------------------------------------------------------------------------
524int 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//--------------------------------------------------------------------------
557int 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//--------------------------------------------------------------------------
593int 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//--------------------------------------------------------------------------
657int 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//--------------------------------------------------------------------------
739void 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//--------------------------------------------------------------------------
788int 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//--------------------------------------------------------------------------
848int 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//--------------------------------------------------------------------------
913Section_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//--------------------------------------------------------------------------
929int 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//--------------------------------------------------------------------------
948int 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//--------------------------------------------------------------------------
997Section_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//--------------------------------------------------------------------------
1034void 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