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