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
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			ALOGE("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	    ALOGE("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		   ALOGE("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		            ALOGE("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			        ALOGE("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//--------------------------------------------------------------------------
315int 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            ALOGE("invalid marker");
363            return FALSE;
364        }
365
366        Sections[SectionsRead].Size = itemlen;
367
368        Data = (uchar *)malloc(itemlen);
369        if (Data == NULL) {
370            ALOGE("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           ALOGE("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                        ALOGE("could not read the rest of the image");
401                        return FALSE;
402                    }
403                    Data = (uchar *)malloc(size);
404                    if (Data == NULL) {
405                        ALOGE("%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                ALOGE("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                            ALOGD("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                        ALOGD("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                    ALOGD("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//--------------------------------------------------------------------------
504void 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//--------------------------------------------------------------------------
520int 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        ALOGE("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        ALOGV("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//--------------------------------------------------------------------------
553int 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        ALOGE("Could not write thumbnail file");
582        return FALSE;
583    }
584}
585
586//--------------------------------------------------------------------------
587// Replace or remove exif thumbnail
588//--------------------------------------------------------------------------
589int 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        ALOGE("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	        ALOGE("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        ALOGE("ReplaceThumbnail successful thumblen %d", ThumbLen);
646#endif
647    return TRUE;
648}
649
650//--------------------------------------------------------------------------
651// Replace or remove exif thumbnail
652//--------------------------------------------------------------------------
653int 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        ALOGE("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	        ALOGE("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	        ALOGE("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        ALOGE("ReplaceThumbnail successful thumblen %d", ThumbLen);
727#endif
728    return TRUE;
729}
730
731
732//--------------------------------------------------------------------------
733// Discard everything but the exif and comment sections.
734//--------------------------------------------------------------------------
735void 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//--------------------------------------------------------------------------
784int WriteJpegFile(const char * FileName)
785{
786    FILE * outfile;
787    int a;
788
789    if (!HaveAll){
790        ALOGE("Can't write back - didn't read all");
791        return FALSE;
792    }
793
794    outfile = fopen(FileName,"wb");
795    if (outfile == NULL){
796        ALOGE("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            ALOGE("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            ALOGE("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//--------------------------------------------------------------------------
844int 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        ALOGE("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//--------------------------------------------------------------------------
909Section_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//--------------------------------------------------------------------------
925int 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//--------------------------------------------------------------------------
944int 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//--------------------------------------------------------------------------
993Section_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        ALOGE("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//--------------------------------------------------------------------------
1030void 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