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