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 //--------------------------------------------------------------------------
Get16m(const void * Short)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 //--------------------------------------------------------------------------
process_COM(const uchar * Data,int length)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 //--------------------------------------------------------------------------
process_SOFn(const uchar * Data,int marker)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 //--------------------------------------------------------------------------
CheckSectionsAllocated(void)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 //--------------------------------------------------------------------------
ReadJpegSections(FILE * infile,ReadMode_t ReadMode)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 //--------------------------------------------------------------------------
ReadJpegSectionsFromBuffer(unsigned char * buffer,unsigned int buffer_size,ReadMode_t ReadMode)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 //--------------------------------------------------------------------------
DiscardData(void)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 //--------------------------------------------------------------------------
ReadJpegFile(const char * FileName,ReadMode_t ReadMode)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 //--------------------------------------------------------------------------
SaveThumbnail(char * ThumbFileName)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 //--------------------------------------------------------------------------
ReplaceThumbnailFromBuffer(const char * Thumb,int ThumbLen)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 //--------------------------------------------------------------------------
ReplaceThumbnail(const char * ThumbFileName)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 //--------------------------------------------------------------------------
DiscardAllButExif(void)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 //--------------------------------------------------------------------------
WriteJpegFile(const char * FileName)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     size_t 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 //--------------------------------------------------------------------------
WriteJpegToBuffer(unsigned char * buffer,unsigned int buffer_size)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 //--------------------------------------------------------------------------
FindSection(int SectionType)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 //--------------------------------------------------------------------------
RemoveSectionType(int SectionType)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 //--------------------------------------------------------------------------
RemoveUnknownSections(void)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 //--------------------------------------------------------------------------
CreateSection(int SectionType,unsigned char * Data,int Size)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 //--------------------------------------------------------------------------
ResetJpgfile(void)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