tizen 2.3.1 release
[framework/web/wearable/wrt-plugins-tizen.git] / src / Exif / JpegFile.cpp
1 //
2 // Tizen Web Device API
3 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 //
19 // For details of JPEG file format see:
20 // http://www.media.mit.edu/pia/Research/deepview/exif.html
21
22 #include "JpegFile.h"
23
24 #include <iomanip>
25 #include <limits>
26 #include <stdio.h>
27 #include <sstream>
28
29 #include <Logger.h>
30 #include <PlatformException.h>
31
32 namespace DeviceAPI {
33
34 using namespace DeviceAPI::Common;
35
36 namespace Exif {
37
38 /**
39  * Size of maximal JPEG's section data length
40  * (since it is stored as unsigned short limit is 2^16 -1)
41  */
42 const unsigned int MAX_JPEG_SECTION_DATA_SIZE = 65535;
43
44 /**
45  * JPEG's section data length includes 2 bytes for length therefore we need to
46  * substract 2 from MAX_JPEG_SECTION_DATA_SIZE
47  */
48 const unsigned int MAX_AVAILABLE_JPEG_SECTION_DATA_SIZE = MAX_JPEG_SECTION_DATA_SIZE - 2;
49
50 bool isJpegMarker(const int value)
51 {
52     return value >= JPEG_MARKER_LOWEST_ID && value <= JPEG_MARKER_HIGHEST_ID;
53 }
54
55 JpegMarker castToJpegMarker(const unsigned char byte)
56 {
57     if (byte < JPEG_MARKER_LOWEST_ID || byte > JPEG_MARKER_HIGHEST_ID) {
58         return JPEG_MARKER_UNKNOWN;
59     }
60
61     return static_cast<JpegMarker>(byte);
62 }
63
64 long readUShortBE(unsigned char* src)
65 {
66     return ((static_cast<long>(src[0]) << 8) | static_cast<long>(src[1]));
67 }
68
69 void writeUShortBE(unsigned short value, unsigned char* buffer)
70 {
71     buffer[0] = static_cast<unsigned char>(value >> 8);
72     buffer[1] = static_cast<unsigned char>(value);
73 }
74
75 struct CArrayDeleter {
76   void operator()(void* buffer) { free(buffer); }
77 };
78
79 JpegFile::JpegFile() :
80     m_in_data(NULL),
81     m_in_data_size(0),
82     m_image_data(NULL),
83     m_image_size(0),
84     m_padding_data(NULL),
85     m_padding_data_size(0),
86     m_in_file(NULL),
87     m_out_file(NULL)
88 {
89
90 }
91
92 JpegFile::~JpegFile()
93 {
94     delete [] m_in_data;
95     m_in_data = NULL;
96     m_in_data_size = 0;
97
98     m_padding_data = NULL;
99     m_padding_data_size = 0;
100
101     for(SectionsVec::iterator it = m_sections.begin(); it != m_sections.end(); ++it) {
102         JpegFileSectionPtr cur = *it;
103
104         if (cur->exif_data) {
105             exif_data_unref(cur->exif_data);
106             cur->exif_data = NULL;
107         }
108
109         cur->data_ptr = NULL;
110         cur->size = 0;
111         cur->type = JPEG_MARKER_UNKNOWN;
112     }
113
114     m_image_data = NULL;
115     m_image_size = 0;
116
117     if (m_in_file) {
118         fclose(m_in_file);
119         m_in_file = NULL;
120     }
121
122     if (m_out_file) {
123         fclose(m_out_file);
124         m_out_file = NULL;
125     }
126 }
127
128 JpegFilePtr JpegFile::loadFile(const std::string& path)
129 {
130     JpegFile* new_jpg = new (std::nothrow) JpegFile();
131     if (!new_jpg) {
132         LOGE("Couldn't allocate Jpegfile!");
133         throw UnknownException("Memory allocation failed");
134     }
135
136     JpegFilePtr jpg_ptr(new_jpg);
137     jpg_ptr->load(path);
138     return jpg_ptr;
139 }
140
141 void JpegFile::load(const std::string& path)
142 {
143     LOGD("Entered file:%s", path.c_str());
144
145     m_source_file_path = path;
146
147     m_in_file = fopen(path.c_str(), "rb");
148     if (!m_in_file) {
149         LOGE("Couldn't open Jpeg file: [%s]", path.c_str());
150         throw NotFoundException("Could not open JPG file");
151     }
152
153     fseek(m_in_file, 0, SEEK_END);
154     long ftell_val = ftell(m_in_file);
155     if (0 > ftell_val) {
156         LOGE("Input file [%s] access error! [%d]", path.c_str(), errno);
157         throw UnknownException("JPEG file is invalid");
158     }
159     const size_t in_file_size = static_cast<size_t>(ftell_val);
160     fseek(m_in_file, 0, SEEK_SET);
161     LOGD("JPEG file: [%s] size:%d", path.c_str(), in_file_size);
162     if (0 == in_file_size) {
163         LOGE("Input file [%s] is empty!", path.c_str());
164         throw UnknownException("JPEG file is invalid");
165     }
166
167     m_in_data = new (std::nothrow) unsigned char[in_file_size];
168     if (!m_in_data) {
169         LOGE("Couldn't allocate buffer with size: %d", in_file_size);
170         throw UnknownException("Memory allocation failed");
171     }
172
173     m_in_data_size = in_file_size;
174
175     const size_t read_bytes = fread(m_in_data, 1, m_in_data_size, m_in_file);
176     if (read_bytes != m_in_data_size) {
177         LOGE("Couldn't read all: %d bytes. Read only: %d bytes!", m_in_data_size,
178                 read_bytes);
179         throw UnknownException("Could not read JPEG file");
180     }
181
182     if (fclose(m_in_file) == EOF) {
183         LOGE("Couldn't close input file: %s!", path.c_str());
184     }
185     m_in_file = NULL;
186
187     generateListOfSections();
188 }
189
190 std::string JpegFile::getPartOfFile(const size_t offset,
191         const size_t num_bytes_before,
192         const size_t num_bytes_after)
193 {
194     long long int start = static_cast<long long int>(offset) - num_bytes_before;
195     if (start < 0) {
196         start = 0;
197     }
198
199     long long int end = static_cast<long long int>(offset) + num_bytes_after;
200     if (end >= m_in_data_size) {
201         end = m_in_data_size - 1;
202     }
203
204     std::stringstream ss;
205     ss << std::setfill('0') << std::setw(2) << std::hex;
206     for(long long int i = start; i <= end; ++i) {
207         ss << static_cast<int>(m_in_data[i]);
208     }
209     return ss.str();
210 }
211
212
213 void JpegFile::generateListOfSections()
214 {
215     LOGD("Entered");
216
217     //JPEG starts with:
218     //FFD8 (2 bytes) - SOI Marker
219     //
220     //then:
221     //N sections - format of section:
222     //0xFF(1 byte) + Marker Number(1 byte) + Data size(2 bytes) + Data
223     //
224     //then:
225     //SOS 0xFF(1 byte) + Marker Number(1 byte) + Data size(2 bytes) + Data
226     //
227     //Image data
228     //
229     //FFD9 (2 bytes) - EOI Marker
230     //
231     //Warning: some images taken on Android contains some extra data at the end
232     //we will keep it in m_padding_data
233
234     m_padding_data = NULL;
235     m_padding_data_size = 0;
236
237     for(size_t offset = 0, iterration = 0; offset < m_in_data_size;++iterration) {
238
239         LOGD("offset:%d | Starting iteration: %d", offset, iterration);
240         const size_t search_len = 10;
241         size_t search_offset = 0;
242         for(search_offset = 0; search_offset < search_len; ++search_offset) {
243             //Skip bytes until first no 0xff
244             unsigned char& tmp_marker = m_in_data[offset + search_offset];
245             if (tmp_marker != 0xff) {
246                 break;
247             }
248         }
249
250         if (search_len == search_offset) {
251             LOGE("offset:%d | Couldn't find marker! RAW DATA:{%s}", offset,
252                     getPartOfFile(offset, 0, 10).c_str());
253             throw UnknownException("JPEG file is invalid");
254         }
255
256         const size_t section_offset = offset + search_offset - 1;
257         unsigned char* section_begin = m_in_data + section_offset;
258
259         offset = section_offset;    //Move to section begin
260         LOGD("offset:%d | Moved to section begin", offset);
261
262         if (!isJpegMarker(section_begin[1])) {
263             LOGE("offset:%d | Is not valid marker: 0x%x RAW DATA:{%s}", offset,
264                     section_begin[1], getPartOfFile(section_offset,0,4).c_str());
265             throw UnknownException("JPEG file is invalid");
266         }
267
268         const JpegMarker cur_marker = castToJpegMarker(section_begin[1]);
269         LOGD("offset:%d | Found valid marker: 0x%x RAW DATA:{%s}", offset,
270                 cur_marker,
271                 getPartOfFile(section_offset,0,4).c_str());
272
273         offset += 2;    //Read 0xffxx marker tag - 2 bytes
274
275         JpegFileSectionPtr section;
276         {
277             JpegFileSection* sec = new (std::nothrow) JpegFileSection();
278             if (!sec) {
279                 LOGE("Couldn't allocate JpegFileSection");
280                 throw UnknownException("Memory allocation failed");
281             }
282
283             section = JpegFileSectionPtr(sec);
284         }
285
286         section->type = cur_marker;
287         m_sections.push_back(section);
288         if (cur_marker == JPEG_MARKER_SOI ||
289             cur_marker == JPEG_MARKER_EOI) {
290             LOGD("offset:%d | Found: %s marker, moving to next marker at:%d",
291                     section_offset, ((cur_marker == JPEG_MARKER_SOI) ? "SOI" : "EOI"),
292                     offset);
293
294             if(cur_marker == JPEG_MARKER_EOI && m_padding_data != NULL) {
295                 LOGW("Padding data have been found - do not try to parse end of file");
296                 break;
297             }
298         }
299         else {
300             //From JPEG/EXIF info:
301             // Please notice that "Data" contains Data size descriptor, if there is
302             // a Marker like this;
303             //
304             // FF C1 00 0C
305             // It means this Marker(0xFFC1) has 0x000C(equal 12)bytes of data. But the
306             // data size '12' includes "Data size" descriptor, it follows only 10 bytes of
307             // data after 0x000C.
308             //
309
310             const long total_section_len = readUShortBE(section_begin + 2); //Include data
311                                                                             //size 2 bytes
312
313             const long section_data_len = total_section_len - 2;            //Exclude data
314                                                                             //size 2 bytes
315
316             LOGD("offset:%d tag:0x%x | Read total_section_len:%d (data len:%d)",
317                     section_offset, cur_marker, total_section_len, section_data_len);
318
319             offset += 2;    //Read data size - 2 bytes
320
321             if (total_section_len < 0) {
322                 LOGE("offset:%d tag:0x%x | Error: total_section_len is: %d < 0", offset,
323                         cur_marker, total_section_len);
324                 throw UnknownException("JPEG file is invalid");
325             }
326
327             if (section_offset + 2 + total_section_len > m_in_data_size) {
328                 LOGE("offset:%d tag:0x%x | Error: current section offset:%d"
329                         " + 2 + total_section_len:%d = %d is greater then file size:%d",
330                         offset, cur_marker,
331                         section_offset, total_section_len,
332                         section_offset + total_section_len, m_in_data_size);
333                 throw UnknownException("JPEG file is invalid");
334             }
335
336             if (JPEG_MARKER_APP1 == cur_marker) {
337                 //TODO: verify this
338                 //-4 --> 0xFF(1 byte)+Marker Number(1 byte)+Data size(2 bytes))
339                 //const unsigned int exif_data_size = section_length - 4;
340
341                 const unsigned int exif_data_size = total_section_len + 2;
342                 section->exif_data = exif_data_new_from_data (section_begin,
343                         exif_data_size);
344
345                 LOGD("offset:%d tag:0x%x | Loading exif from offset:%d"
346                         " len:%d exif_data_new_from_data returned: %p",
347                         offset, cur_marker, section_offset, exif_data_size,
348                         section->exif_data);
349
350                 if (!section->exif_data) {
351                     LOGW("offset:%d tag:0x%x | Couldn't load Exif!", offset, cur_marker);
352                 }
353             }
354
355             //This just saves pointer not copying data
356             section->data_ptr = section_begin + 2 + 2; //2 bytes marker + 2 bytes data size
357             section->size = section_data_len;  //Exclude data size
358
359             if (JPEG_MARKER_SOS == cur_marker) {
360                 //Calculate offset of first image data which is just after this SOS section
361                 const size_t image_data_offset = section_offset + 2 + total_section_len;
362
363                 //Calculate size of image data from start to expected EOI at end of file.
364                 //
365                 //-2 (exclude ending EOI marker (2 bytes)
366                 size_t image_size = m_in_data_size - image_data_offset - 2;
367                 LOGW("offset:%d tag:0x%x | Image data offset:%d Estimated image size:%d",
368                         offset, cur_marker, image_data_offset, image_size);
369
370                 m_image_data = m_in_data + image_data_offset;
371
372                 size_t eoi_tag_index = 0;
373                 bool found_eoi_tag = searchForTagInBuffer(m_in_data + image_data_offset,
374                         m_in_data + m_in_data_size, JPEG_MARKER_EOI, eoi_tag_index);
375                 if(!found_eoi_tag) {
376                     LOGE("Could not find EOI tag! Assume that there is no EOI and rest of "
377                             "JPEG file contains image data stream: image_size+= 2");
378                     image_size += 2; //Skip expected EOI tag which is not present
379                 } else {
380                     LOGD("EOI tag found at offset: %d from SOS data", eoi_tag_index);
381
382                     if(eoi_tag_index != image_size) {
383                         LOGW("Estimated image size:%d doesn't match EOI tag index:%d"
384                                 " delta:%d", image_size, eoi_tag_index,
385                                 image_size - eoi_tag_index);
386
387                         LOGW("Setting image_size to EOI tag: %d", eoi_tag_index);
388                         image_size = eoi_tag_index;
389
390                         m_padding_data = m_image_data + image_size + 2; //(skip EOI tag)
391                         m_padding_data_size = (m_in_data + m_in_data_size) - m_padding_data;
392                         LOGW("Saving padding data from offset:%d with size:%d",
393                             m_padding_data - m_in_data, m_padding_data_size);
394                     }
395                 }
396
397                 m_image_size = image_size;
398
399                 offset = image_data_offset + image_size;
400                 LOGD("offset:%d tag:0x%x | SOS Offset moved to next marker", offset,
401                         cur_marker);
402             }
403             else {
404                 offset += section_data_len;
405                 LOGD("offset:%d tag:0x%x | Offset moved to next marker", offset, cur_marker);
406             }
407         }
408     }
409 }
410
411 bool JpegFile::searchForTagInBuffer(const unsigned char* buffer_start,
412         const unsigned char* buffer_end,
413         const JpegMarker marker,
414         size_t& out_index)
415 {
416     LOGD("Entered start:%p end:%p marker:0x%x", buffer_start, buffer_end, marker);
417
418     if(!buffer_start) {
419         LOGE("buffer_start is NULL");
420         return false;
421     }
422
423     if(!buffer_end) {
424         LOGE("buffer_end is NULL");
425         return false;
426     }
427
428     if(buffer_end <= buffer_start) {
429         LOGE("buffer_end: %p <= buffer_start: %p", buffer_end, buffer_start);
430         return false;
431     }
432
433     LOGD("Bytes to scan: %d", static_cast<size_t>(buffer_end - buffer_start));
434     const unsigned char marker_uchar = static_cast<unsigned char>(marker);
435
436     for(const unsigned char* ptr = buffer_start; ptr < buffer_end; ++ptr) {
437
438         if((0xff == *ptr) && (ptr+1 < buffer_end)) {
439             if(marker_uchar == *(ptr+1)) {
440                 out_index = static_cast<size_t>(ptr - buffer_start);
441                 return true;
442             }
443         }
444     }
445
446     out_index = 0;
447     return false;
448 }
449
450 void JpegFile::setNewExifData(ExifData* new_exif_data)
451 {
452     if (!new_exif_data) {
453         LOGE("Trying to set NULL exif_data!");
454         throw UnknownException("Could not save Exif in JPEG file");
455     }
456
457     JpegFileSectionPtr exif = getExifSection();
458     if (!exif) {
459         LOGW("Could't find Exif section - creating new one");
460         {
461             JpegFileSection* new_sec = new (std::nothrow) JpegFileSection();
462             if (!new_sec) {
463                 LOGE("Couldn't allocate JpegFileSection");
464                 throw UnknownException("Memory allocation failed");
465             }
466             new_sec->type = JPEG_MARKER_APP1;
467
468             exif = JpegFileSectionPtr(new_sec);
469         }
470
471         SectionsVec::iterator insert_it = m_sections.begin();
472         bool soi_is_present = false;
473
474         if (insert_it != m_sections.end()) {
475             if ((*insert_it)->type != JPEG_MARKER_SOI) {
476                 LOGW("First section is not SOI - Start Of Image!");
477             }
478             else {
479                 soi_is_present = true;
480             }
481         }
482
483         if (!soi_is_present) {
484             LOGW("SOI section is missing");
485             throw UnknownException("JPEG file is invalid");
486         }
487
488         //Insert new Exif sections just after SOI
489         ++insert_it;
490         if (insert_it != m_sections.begin()) {
491             m_sections.insert(insert_it, exif);
492         }
493         else {
494             //This shouldn't happen since we at lest need SOS and EOI sections
495             m_sections.push_back(exif);
496         }
497     }
498
499     //We don't want to save old data
500     exif->data_ptr = NULL;
501     exif->size = 0;
502
503     exif_data_unref(exif->exif_data);
504     exif_data_ref (new_exif_data);
505     exif->exif_data = new_exif_data;
506 }
507
508 ExifData* JpegFile::getExifData()
509 {
510     JpegFileSectionPtr exif = getExifSection();
511     if (!exif) {
512         return NULL;
513     }
514
515     exif_data_ref(exif->exif_data);
516     return exif->exif_data;
517 }
518
519 void JpegFile::saveToFile(const std::string& out_path)
520 {
521     LOGD("Entered out_path:%s", out_path.c_str());
522     try {
523         saveToFilePriv(out_path);
524     }
525     catch (...) {
526         LOGE("Exception occured during saveToFilePriv "
527                 "original file: [%] new: [%s]",
528                 m_source_file_path.c_str(),
529                 out_path.c_str());
530
531         if (out_path == m_source_file_path) {
532
533             LOGD("Trying to recover broken JPEG file: [%s]", out_path.c_str());
534             //We were writing to source file and since something went wrong let's
535             //restore old file - we have it in m_in_data
536
537             FILE* outf = fopen(out_path.c_str(), "wb");
538             if (!outf) {
539                 LOGE("Couldn't open output file: [%s] - JPEG file will not be restored!");
540             }
541             else {
542                 size_t bytes_wrote = fwrite(m_in_data, 1, m_in_data_size, outf);
543                 if (bytes_wrote != m_in_data_size) {
544                     LOGE("Couldn't restore whole JPEG! "
545                             "Only %d of %d bytes have been wrote!",
546                             bytes_wrote, m_in_data_size);
547                 }
548                 if (EOF == fclose(outf)) {
549                     LOGE("Couldn't close restore output file: [%s]", out_path.c_str());
550                 }
551             }
552         }
553
554         throw;
555     }
556 }
557
558 void JpegFile::saveToFilePriv(const std::string& out_path)
559 {
560     LOGD("Entered out_path:%s", out_path.c_str());
561
562     m_out_file = fopen(out_path.c_str(), "wb");
563     if (!m_out_file) {
564         LOGE("Couldn't open output file: %s", out_path.c_str());
565         throw UnknownException("Could not write JPEG file");
566     }
567
568     unsigned char tmp_buf[128];
569     size_t offset = 0;
570
571     int section_index = 0;
572     for(SectionsVec::iterator it = m_sections.begin();
573             it != m_sections.end();
574             ++it, ++ section_index) {
575
576         JpegFileSectionPtr cur = *it;
577         const JpegMarker cur_marker = cur->type;
578
579         LOGD("offset:%d | Section: %d marker 0x%x", offset, section_index, cur_marker);
580
581         size_t bytes_to_write = 0;
582         size_t bytes_wrote = 0;
583
584         tmp_buf[0] = 0xff;
585         tmp_buf[1] = cur_marker;
586         bytes_to_write += 2;
587
588         bool write_section_data = false;
589
590         bool write_exif_data = false;
591
592         std::unique_ptr<unsigned char, CArrayDeleter> exif_output_data;
593         unsigned int exif_output_size = 0;
594
595         if (cur_marker != JPEG_MARKER_SOI &&
596                 cur_marker != JPEG_MARKER_EOI) {
597
598             unsigned short section_size = 2;
599             if (JPEG_MARKER_APP1 && cur->exif_data) {
600
601                 unsigned char* tmp = NULL;
602                 exif_data_save_data (cur->exif_data, &tmp, &exif_output_size);
603                 if (!tmp || 0 == exif_output_size) {
604                     LOGE("Couldn't generate RAW Exif data!");
605                     throw UnknownException("Could not save Exif in JPEG file");
606                 }
607
608                 LOGD("offset:%d | Generated Exif RAW Data length:%d", offset,
609                         exif_output_size);
610
611                 exif_output_data.reset(tmp);
612
613                 if (exif_output_size > MAX_AVAILABLE_JPEG_SECTION_DATA_SIZE) {
614                     LOGE("exif_output_size:%d is greater then maximum JPEG section"
615                             "data block size: %d", exif_output_size,
616                             MAX_AVAILABLE_JPEG_SECTION_DATA_SIZE);
617                     throw UnknownException("Exif data is to big to be saved in JPEG file");
618                 }
619                 section_size += exif_output_size;
620                 write_exif_data = true;
621             }
622             else {
623                 section_size += cur->size;
624                 write_section_data = true;
625             }
626
627             writeUShortBE(section_size, tmp_buf + bytes_to_write);
628             bytes_to_write += 2;
629         }
630
631         LOGD("offset:%d | Writing section: marker:0x%x size:%d", offset, cur_marker,
632                 cur->size);
633
634         bytes_wrote = fwrite(tmp_buf, 1, bytes_to_write, m_out_file);
635         offset += bytes_wrote;
636
637         if (bytes_wrote != bytes_to_write) {
638             LOGE("Couldn't wrote %d bytes! Only %d bytes wrote", bytes_to_write,
639                     bytes_wrote);
640             throw UnknownException("Could not write JPEG file");
641         }
642
643         if (write_section_data && cur->size > 0) {
644             LOGD("offset:%d | Writing data with length:%d", offset, cur->size);
645
646             bytes_to_write = cur->size;
647             bytes_wrote = fwrite(cur->data_ptr, 1, bytes_to_write, m_out_file);
648             offset += bytes_wrote;
649
650             if (bytes_wrote != bytes_to_write) {
651                 LOGE("Couldn't wrote %d bytes! Only %d bytes wrote", bytes_to_write,
652                         bytes_wrote);
653                 throw UnknownException("Could not write JPEG file");
654             }
655         }
656
657         if (write_exif_data && exif_output_data && exif_output_size > 0) {
658             LOGD("offset:%d | Writing new exif data with length:%d", offset,
659                     exif_output_size);
660
661             bytes_to_write = exif_output_size;
662             bytes_wrote = fwrite(exif_output_data.get(), 1, bytes_to_write, m_out_file);
663             offset += bytes_wrote;
664
665             if (bytes_wrote != bytes_to_write) {
666                 LOGE("Couldn't wrote %d bytes! Only %d bytes wrote", bytes_to_write,
667                         bytes_wrote);
668                 throw UnknownException("Could not write JPEG file");
669             }
670         }
671
672         if (JPEG_MARKER_SOS == cur_marker) {
673             LOGD("offset:%d | Writing image data stream with lenght:%d", offset,
674                     m_image_size);
675
676             bytes_to_write = m_image_size;
677             bytes_wrote = fwrite(m_image_data, 1, bytes_to_write, m_out_file);
678             offset += bytes_wrote;
679
680             if (bytes_wrote != bytes_to_write) {
681                 LOGE("Couldn't wrote %d bytes! Only %d bytes wrote", bytes_to_write,
682                         bytes_wrote);
683                 throw UnknownException("Could not write JPEG file");
684             }
685         }
686     }
687
688     if(m_padding_data && m_padding_data_size > 0) {
689         LOGD("Padding data exists and contains:%d bytes saving to JPEG file");
690         const size_t bytes_wrote = fwrite(m_image_data, 1, m_padding_data_size,
691                 m_out_file);
692
693         if (bytes_wrote != m_padding_data_size) {
694             LOGE("Couldn't wrote %d bytes! Only %d bytes wrote", m_padding_data_size,
695                     bytes_wrote);
696             throw UnknownException("Could not write JPEG file");
697         }
698     }
699
700     if (fclose(m_out_file) == EOF) {
701         LOGE("Couldn't close output file: %s", out_path.c_str());
702         m_out_file = NULL;
703     }  else {
704         m_out_file = NULL;
705         LOGD("Closed output file: %s wrote:%d bytes: %d", out_path.c_str(), offset);
706     }
707 }
708
709 JpegFileSectionPtr JpegFile::getExifSection()
710 {
711     size_t num_exif_sections = 0;
712     JpegFileSectionPtr first_exif_section;
713
714     for(SectionsVec::iterator it = m_sections.begin(); it != m_sections.end(); ++it) {
715         JpegFileSectionPtr cur = *it;
716
717         if (JPEG_MARKER_APP1 == cur->type) {
718             if (!cur->exif_data) {
719                 LOGW("Warning: found APP1 section but exif_data is NULL (Not Exif?)");
720                 continue;
721             }
722
723             ++num_exif_sections;
724             if (!first_exif_section) {
725                 first_exif_section = cur;
726             }
727             else {
728                 LOGW("Warning: found %d APP1/Exif sections - only first is currently supported!");
729             }
730         }
731     }
732
733     return first_exif_section;
734 }
735
736
737 } // namespace Exif
738 } // namespace DeviceAPI