2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 //--------------------------------------------------------------------------
10 // This module gathers information about a digital image file. This includes:
11 // - File name and path
13 // - Resolution (if available)
14 // - IPTC information (if available)
15 // - EXIF information (if available)
16 // All gathered information is stored in a vector of 'description' and 'value'
17 // pairs (where both description and value fields are of CStdString types).
18 //--------------------------------------------------------------------------
20 #include "JpegParse.h"
22 #include "filesystem/File.h"
29 typedef unsigned char BYTE
;
33 #define min(a,b) (a)>(b)?(b):(a)
36 using namespace XFILE
;
38 //--------------------------------------------------------------------------
39 #define JPEG_PARSE_STRING_ID_BASE 21500
41 ProcessUnknown
= JPEG_PARSE_STRING_ID_BASE
,
60 //--------------------------------------------------------------------------
62 //--------------------------------------------------------------------------
63 CJpegParse::CJpegParse():
66 memset(&m_ExifInfo
, 0, sizeof(m_ExifInfo
));
67 memset(&m_IPTCInfo
, 0, sizeof(m_IPTCInfo
));
70 //--------------------------------------------------------------------------
71 // Process a SOFn marker. This is useful for the image dimensions
72 //--------------------------------------------------------------------------
73 void CJpegParse::ProcessSOFn (void)
75 m_ExifInfo
.Height
= CExifParse::Get16(m_SectionBuffer
+3);
76 m_ExifInfo
.Width
= CExifParse::Get16(m_SectionBuffer
+5);
78 unsigned char num_components
= m_SectionBuffer
[7];
79 if (num_components
!= 3)
81 m_ExifInfo
.IsColor
= 0;
85 m_ExifInfo
.IsColor
= 1;
90 //--------------------------------------------------------------------------
91 // Read a section from a JPEG file. Note that this function allocates memory.
92 // It must be called in pair with ReleaseSection
93 //--------------------------------------------------------------------------
94 bool CJpegParse::GetSection (CFile
& infile
, const unsigned short sectionLength
)
96 if (sectionLength
< 2)
98 printf("JpgParse: invalid section length");
102 m_SectionBuffer
= new unsigned char[sectionLength
];
103 if (m_SectionBuffer
== NULL
)
105 printf("JpgParse: could not allocate memory");
108 // Store first two pre-read bytes.
109 m_SectionBuffer
[0] = (unsigned char)(sectionLength
>> 8);
110 m_SectionBuffer
[1] = (unsigned char)(sectionLength
& 0x00FF);
112 unsigned int len
= (unsigned int)sectionLength
;
114 size_t bytesRead
= infile
.Read(m_SectionBuffer
+sizeof(sectionLength
), len
-sizeof(sectionLength
));
115 if (bytesRead
!= sectionLength
-sizeof(sectionLength
))
117 printf("JpgParse: premature end of file?");
124 //--------------------------------------------------------------------------
125 // Deallocate memory allocated in GetSection. This function must always
126 // be paired by a preceding GetSection call.
127 //--------------------------------------------------------------------------
128 void CJpegParse::ReleaseSection (void)
130 delete[] m_SectionBuffer
;
131 m_SectionBuffer
= NULL
;
134 //--------------------------------------------------------------------------
135 // Parse the marker stream until SOS or EOI is seen; infile has already been
137 //--------------------------------------------------------------------------
138 bool CJpegParse::ExtractInfo (CFile
& infile
)
140 // Get file marker (two bytes - must be 0xFFD8 for JPEG files
142 size_t bytesRead
= infile
.Read(&a
, sizeof(BYTE
));
143 if ((bytesRead
!= sizeof(BYTE
)) || (a
!= 0xFF))
147 bytesRead
= infile
.Read(&a
, sizeof(BYTE
));
148 if ((bytesRead
!= sizeof(BYTE
)) || (a
!= M_SOI
))
156 for (a
=0; a
<7; a
++) {
157 bytesRead
= infile
.Read(&marker
, sizeof(BYTE
));
163 printf("JpgParse: too many padding bytes");
169 // Read the length of the section.
170 unsigned short itemlen
= 0;
171 bytesRead
= infile
.Read(&itemlen
, sizeof(itemlen
));
172 itemlen
= CExifParse::Get16(&itemlen
);
174 if ((bytesRead
!= sizeof(itemlen
)) || (itemlen
< sizeof(itemlen
)))
176 printf("JpgParse: invalid marker");
182 case M_SOS
: // stop before hitting compressed data
185 case M_EOI
: // in case it's a tables-only JPEG stream
186 printf("JpgParse: No image in jpeg!");
190 case M_COM
: // Comment section
191 GetSection(infile
, itemlen
);
192 if (m_SectionBuffer
!= NULL
)
194 // CExifParse::FixComment(comment); // Ensure comment is printable
195 unsigned short length
= min(itemlen
- 2, MAX_COMMENT
);
196 strncpy(m_ExifInfo
.FileComment
, (char *)&m_SectionBuffer
[2], length
);
197 m_ExifInfo
.FileComment
[length
] = '\0';
215 GetSection(infile
, itemlen
);
216 if ((m_SectionBuffer
!= NULL
) && (itemlen
>= 7))
219 m_ExifInfo
.Process
= marker
;
225 GetSection(infile
, itemlen
);
226 if (m_SectionBuffer
!= NULL
)
228 CIptcParse::Process(m_SectionBuffer
, itemlen
, &m_IPTCInfo
);
234 // Seen files from some 'U-lead' software with Vivitar scanner
235 // that uses marker 31 for non exif stuff. Thus make sure
236 // it says 'Exif' in the section before treating it as exif.
237 GetSection(infile
, itemlen
);
238 if (m_SectionBuffer
!= NULL
)
241 exif
.Process(m_SectionBuffer
, itemlen
, &m_ExifInfo
);
247 // Regular jpegs always have this tag, exif images have the exif
248 // marker instead, although ACDsee will write images with both markers.
249 // this program will re-create this marker on absence of exif marker.
250 // hence no need to keep the copy from the file.
251 // fall through to default case
253 // Skip any other sections.
254 GetSection(infile
, itemlen
);
262 //--------------------------------------------------------------------------
263 // Process a file. Check if it is JPEG. Extract exif/iptc info if it is.
264 //--------------------------------------------------------------------------
265 bool CJpegParse::Process (const char *picFileName
)
269 if (!file
.Open(picFileName
))
272 // File exists and successfully opened. Start processing
273 // Gather all information about the file
275 /* // Get file name...
276 CStdString tmp, urlFName, path;
277 CURL url(picFileName);
278 url.GetURLWithoutUserDetails(urlFName);
279 CUtil::Split(urlFName, path, tmp);
280 m_JpegInfo[SLIDESHOW_FILE_NAME] = tmp;
282 m_JpegInfo[SLIDESHOW_FILE_PATH] = path;
286 CFile::Stat(picFileName, &fileStat);
287 float fileSize = (float)fileStat.st_size;
304 tmp.Format("%.2f %s", fileSize, tmp);
305 m_JpegInfo[SLIDESHOW_FILE_SIZE] = tmp;
307 // ...then date and time...
308 CDateTime date((time_t)fileStat.st_mtime);
309 tmp.Format("%s %s", date.GetAsLocalizedDate(), date.GetAsLocalizedTime());
310 m_JpegInfo[SLIDESHOW_FILE_DATE] = tmp;*/
312 bool result
= ExtractInfo(file
);