Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87c343b4f1 | ||
|
|
af81c87222 | ||
|
|
36f7056799 |
@@ -1,8 +1,4 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(VERSION 2.6)
|
||||
if(POLICY CMP0063)
|
||||
cmake_policy(SET CMP0063 OLD)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
project(TinyEXIF)
|
||||
include(GNUInstallDirs)
|
||||
@@ -14,12 +10,13 @@ find_package(tinyxml2 REQUIRED)
|
||||
################################
|
||||
# set lib version here
|
||||
|
||||
set(GENERIC_LIB_VERSION "1.0.0")
|
||||
set(GENERIC_LIB_VERSION "1.0.1")
|
||||
set(GENERIC_LIB_SOVERSION "1")
|
||||
|
||||
################################
|
||||
# Add definitions
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
|
||||
################################
|
||||
|
||||
22
README.md
22
README.md
@@ -38,3 +38,25 @@ int main(int argc, const char** argv) {
|
||||
}
|
||||
```
|
||||
See `main.cpp` for more details.
|
||||
|
||||
## Copyright
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
148
TinyEXIF.cpp
148
TinyEXIF.cpp
@@ -13,22 +13,22 @@
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
-- Redistributions of source code must retain the above copyright notice,
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
-- Redistributions in binary form must reproduce the above copyright notice,
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "TinyEXIF.h"
|
||||
@@ -258,12 +258,12 @@ public:
|
||||
// Constructors
|
||||
EXIFInfo::EXIFInfo() : Fields(FIELD_NA) {
|
||||
}
|
||||
EXIFInfo::EXIFInfo(EXIFStream& stream) {
|
||||
parseFrom(stream);
|
||||
}
|
||||
EXIFInfo::EXIFInfo(const uint8_t* data, unsigned length) {
|
||||
parseFrom(data, length);
|
||||
}
|
||||
EXIFInfo::EXIFInfo(const std::string& data) {
|
||||
parseFrom(data);
|
||||
}
|
||||
|
||||
|
||||
// Parse tag as Image IFD
|
||||
@@ -615,18 +615,16 @@ void EXIFInfo::parseIFDGPS(EntryParser& parser) {
|
||||
// Locates the JM_APP1 segment and parses it using
|
||||
// parseFromEXIFSegment() or parseFromXMPSegment()
|
||||
//
|
||||
int EXIFInfo::parseFrom(const uint8_t* buf, unsigned len) {
|
||||
int EXIFInfo::parseFrom(EXIFStream& stream) {
|
||||
clear();
|
||||
if (!stream.IsValid())
|
||||
return PARSE_INVALID_JPEG;
|
||||
|
||||
// Sanity check: all JPEG files start with 0xFFD8 and end with 0xFFD9
|
||||
// This check also ensures that the user has supplied a correct value for len.
|
||||
if (!buf || len < 16)
|
||||
const uint8_t* buf(stream.GetBuffer(2));
|
||||
if (buf == NULL || buf[0] != JM_START || buf[1] != JM_SOI)
|
||||
return PARSE_INVALID_JPEG;
|
||||
if (buf[0] != JM_START || buf[1] != JM_SOI)
|
||||
return PARSE_INVALID_JPEG;
|
||||
// not always valid, sometimes 0xFF is added for padding
|
||||
//if (buf[len-2] != JM_START || buf[len-1] != JM_EOI)
|
||||
// return PARSE_INVALID_JPEG;
|
||||
|
||||
// Scan for JM_APP1 header (bytes 0xFF 0xE1) and parse its length.
|
||||
// Exit if both EXIF and XMP sections were parsed.
|
||||
@@ -637,19 +635,20 @@ int EXIFInfo::parseFrom(const uint8_t* buf, unsigned len) {
|
||||
inline operator uint32_t& () { return val; }
|
||||
inline int operator () (int code=PARSE_ABSENT_DATA) const { return val&FIELD_ALL ? (int)PARSE_SUCCESS : code; }
|
||||
} app1s(Fields);
|
||||
for (unsigned pos=2; pos<len; ) {
|
||||
// find next marker
|
||||
uint8_t marker, prev(0);
|
||||
do {
|
||||
marker = buf[pos++];
|
||||
if (marker != JM_START && prev == JM_START)
|
||||
break;
|
||||
prev = marker;
|
||||
} while (pos<len);
|
||||
while ((buf=stream.GetBuffer(2)) != NULL) {
|
||||
// find next marker;
|
||||
// in cases of markers appended after the compressed data,
|
||||
// optional JM_START fill bytes may precede the marker
|
||||
if (*buf++ != JM_START)
|
||||
break;
|
||||
uint8_t marker;
|
||||
while ((marker=buf[0]) == JM_START && (buf=stream.GetBuffer(1)) != NULL);
|
||||
// select marker
|
||||
uint16_t sectionLength;
|
||||
switch (marker) {
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case JM_START:
|
||||
case JM_RST0:
|
||||
case JM_RST1:
|
||||
case JM_RST2:
|
||||
@@ -663,12 +662,15 @@ int EXIFInfo::parseFrom(const uint8_t* buf, unsigned len) {
|
||||
case JM_SOS: // start of stream: and we're done
|
||||
case JM_EOI: // no data? not good
|
||||
return app1s();
|
||||
case JM_APP1: {
|
||||
const uint16_t section_length(EntryParser::parse16(buf + pos, false));
|
||||
int ret;
|
||||
switch (ret=parseFromEXIFSegment(buf + pos + 2, section_length - 2)) {
|
||||
case JM_APP1:
|
||||
if ((buf=stream.GetBuffer(2)) == NULL)
|
||||
return app1s(PARSE_INVALID_JPEG);
|
||||
sectionLength = EntryParser::parse16(buf, false);
|
||||
if (sectionLength <= 2 || (buf=stream.GetBuffer(sectionLength-=2)) == NULL)
|
||||
return app1s(PARSE_INVALID_JPEG);
|
||||
switch (int ret=parseFromEXIFSegment(buf, sectionLength)) {
|
||||
case PARSE_ABSENT_DATA:
|
||||
switch (ret=parseFromXMPSegment(buf + pos + 2, section_length - 2)) {
|
||||
switch (ret=parseFromXMPSegment(buf, sectionLength)) {
|
||||
case PARSE_ABSENT_DATA:
|
||||
break;
|
||||
case PARSE_SUCCESS:
|
||||
@@ -686,22 +688,42 @@ int EXIFInfo::parseFrom(const uint8_t* buf, unsigned len) {
|
||||
default:
|
||||
return app1s(ret); // some error
|
||||
}
|
||||
}
|
||||
default: {
|
||||
// read section length
|
||||
const uint16_t section_length(EntryParser::parse16(buf + pos, false));
|
||||
if (pos + section_length > len)
|
||||
return app1s(PARSE_INVALID_JPEG);
|
||||
break;
|
||||
default:
|
||||
// skip the section
|
||||
pos += section_length;
|
||||
}
|
||||
if ((buf=stream.GetBuffer(2)) == NULL ||
|
||||
(sectionLength=EntryParser::parse16(buf, false)) <= 2 ||
|
||||
!stream.SkipBuffer(sectionLength-2))
|
||||
return app1s(PARSE_INVALID_JPEG);
|
||||
}
|
||||
}
|
||||
return app1s();
|
||||
}
|
||||
|
||||
int EXIFInfo::parseFrom(const std::string& data) {
|
||||
return parseFrom((const uint8_t*)data.data(), (unsigned)data.length());
|
||||
int EXIFInfo::parseFrom(const uint8_t* buf, unsigned len) {
|
||||
class EXIFStreamBuffer : public EXIFStream {
|
||||
public:
|
||||
explicit EXIFStreamBuffer(const uint8_t* buf, unsigned len)
|
||||
: it(buf), end(buf+len) {}
|
||||
bool IsValid() const override {
|
||||
return it != NULL;
|
||||
}
|
||||
const uint8_t* GetBuffer(unsigned desiredLength) override {
|
||||
const uint8_t* const itNext(it+desiredLength);
|
||||
if (itNext >= end)
|
||||
return NULL;
|
||||
const uint8_t* const begin(it);
|
||||
it = itNext;
|
||||
return begin;
|
||||
}
|
||||
bool SkipBuffer(unsigned desiredLength) override {
|
||||
return GetBuffer(desiredLength) != NULL;
|
||||
}
|
||||
private:
|
||||
const uint8_t* it, * const end;
|
||||
};
|
||||
EXIFStreamBuffer stream(buf, len);
|
||||
return parseFrom(stream);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -851,7 +873,7 @@ int EXIFInfo::parseFromXMPSegment(const uint8_t* buf, unsigned len) {
|
||||
if ((strEnd=Tools::strrnstr(strXMP, "<?xpacket end=", len)) != NULL)
|
||||
len = (unsigned)(strEnd - strXMP);
|
||||
|
||||
// Now try parsing the XML packet.
|
||||
// Try parsing the XML packet.
|
||||
tinyxml2::XMLDocument doc;
|
||||
const tinyxml2::XMLElement* document;
|
||||
if (doc.Parse(strXMP, len) != tinyxml2::XML_SUCCESS ||
|
||||
@@ -860,7 +882,26 @@ int EXIFInfo::parseFromXMPSegment(const uint8_t* buf, unsigned len) {
|
||||
(document=document->FirstChildElement("rdf:Description")) == NULL)
|
||||
return PARSE_ABSENT_DATA;
|
||||
|
||||
// Now try parsing the XMP content for projection type.
|
||||
// Try parsing the XMP content for tiff details.
|
||||
if (Orientation == 0) {
|
||||
uint32_t _Orientation(0);
|
||||
document->QueryUnsignedAttribute("tiff:Orientation", &_Orientation);
|
||||
Orientation = (uint16_t)_Orientation;
|
||||
}
|
||||
if (ImageWidth == 0 && ImageHeight == 0) {
|
||||
document->QueryUnsignedAttribute("tiff:ImageWidth", &ImageWidth);
|
||||
if (document->QueryUnsignedAttribute("tiff:ImageHeight", &ImageHeight) != tinyxml2::XML_SUCCESS)
|
||||
document->QueryUnsignedAttribute("tiff:ImageLength", &ImageHeight) ;
|
||||
}
|
||||
if (XResolution == 0 && YResolution == 0 && ResolutionUnit == 0) {
|
||||
document->QueryDoubleAttribute("tiff:XResolution", &XResolution);
|
||||
document->QueryDoubleAttribute("tiff:YResolution", &YResolution);
|
||||
uint32_t _ResolutionUnit(0);
|
||||
document->QueryUnsignedAttribute("tiff:ResolutionUnit", &_ResolutionUnit);
|
||||
ResolutionUnit = (uint16_t)_ResolutionUnit;
|
||||
}
|
||||
|
||||
// Try parsing the XMP content for projection type.
|
||||
{
|
||||
const tinyxml2::XMLElement* const element(document->FirstChildElement("GPano:ProjectionType"));
|
||||
if (element != NULL) {
|
||||
@@ -875,7 +916,7 @@ int EXIFInfo::parseFromXMPSegment(const uint8_t* buf, unsigned len) {
|
||||
}
|
||||
}
|
||||
|
||||
// Now try parsing the XMP content for DJI info.
|
||||
// Try parsing the XMP content for DJI info.
|
||||
document->QueryDoubleAttribute("drone-dji:AbsoluteAltitude", &GeoLocation.Altitude);
|
||||
document->QueryDoubleAttribute("drone-dji:RelativeAltitude", &GeoLocation.RelativeAltitude);
|
||||
document->QueryDoubleAttribute("drone-dji:GimbalRollDegree", &GeoLocation.RollDegree);
|
||||
@@ -887,7 +928,7 @@ int EXIFInfo::parseFromXMPSegment(const uint8_t* buf, unsigned len) {
|
||||
|
||||
|
||||
void EXIFInfo::Geolocation_t::parseCoords() {
|
||||
// convert GPS latitude
|
||||
// Convert GPS latitude
|
||||
if (LatComponents.degrees != DBL_MAX ||
|
||||
LatComponents.minutes != 0 ||
|
||||
LatComponents.seconds != 0) {
|
||||
@@ -898,7 +939,7 @@ void EXIFInfo::Geolocation_t::parseCoords() {
|
||||
if ('S' == LatComponents.direction)
|
||||
Latitude = -Latitude;
|
||||
}
|
||||
// convert GPS longitude
|
||||
// Convert GPS longitude
|
||||
if (LonComponents.degrees != DBL_MAX ||
|
||||
LonComponents.minutes != 0 ||
|
||||
LonComponents.seconds != 0) {
|
||||
@@ -909,7 +950,7 @@ void EXIFInfo::Geolocation_t::parseCoords() {
|
||||
if ('W' == LonComponents.direction)
|
||||
Longitude = -Longitude;
|
||||
}
|
||||
// convert GPS altitude
|
||||
// Convert GPS altitude
|
||||
if (hasAltitude() &&
|
||||
AltitudeRef == 1) {
|
||||
Altitude = -Altitude;
|
||||
@@ -951,6 +992,9 @@ void EXIFInfo::clear() {
|
||||
RelatedImageWidth = 0;
|
||||
RelatedImageHeight= 0;
|
||||
Orientation = 0;
|
||||
XResolution = 0;
|
||||
YResolution = 0;
|
||||
ResolutionUnit = 0;
|
||||
BitsPerSample = 0;
|
||||
ExposureTime = 0;
|
||||
FNumber = 0;
|
||||
|
||||
52
TinyEXIF.h
52
TinyEXIF.h
@@ -13,22 +13,22 @@
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
-- Redistributions of source code must retain the above copyright notice,
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
-- Redistributions in binary form must reproduce the above copyright notice,
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __TINYEXIF_H__
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
#define TINYEXIF_MAJOR_VERSION 1
|
||||
#define TINYEXIF_MINOR_VERSION 0
|
||||
#define TINYEXIF_PATCH_VERSION 0
|
||||
#define TINYEXIF_PATCH_VERSION 1
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# ifdef TINYEXIF_EXPORT
|
||||
@@ -75,22 +75,42 @@ enum FieldCode {
|
||||
class EntryParser;
|
||||
|
||||
//
|
||||
// Class responsible for storing and parsing EXIF information from a JPEG blob
|
||||
// Interface class responsible for fetching stream data to be parsed
|
||||
//
|
||||
class TINYEXIF_LIB EXIFStream {
|
||||
public:
|
||||
virtual ~EXIFStream() {}
|
||||
|
||||
// Check the state of the stream.
|
||||
virtual bool IsValid() const = 0;
|
||||
|
||||
// Return the pointer to the beginning of the desired size buffer
|
||||
// following current buffer position.
|
||||
virtual const uint8_t* GetBuffer(unsigned desiredLength) = 0;
|
||||
|
||||
// Advance current buffer position with the desired size;
|
||||
// return false if stream ends in less than the desired size.
|
||||
virtual bool SkipBuffer(unsigned desiredLength) = 0;
|
||||
};
|
||||
|
||||
//
|
||||
// Class responsible for storing and parsing EXIF & XMP metadata from a JPEG stream
|
||||
//
|
||||
class TINYEXIF_LIB EXIFInfo {
|
||||
public:
|
||||
EXIFInfo();
|
||||
EXIFInfo(EXIFStream& stream);
|
||||
EXIFInfo(const uint8_t* data, unsigned length);
|
||||
EXIFInfo(const std::string& data);
|
||||
|
||||
// Parsing function for an entire JPEG image stream.
|
||||
//
|
||||
// PARAM 'stream': Interface to fetch JPEG image stream.
|
||||
// PARAM 'data': A pointer to a JPEG image.
|
||||
// PARAM 'length': The length of the JPEG image.
|
||||
// RETURN: PARSE_SUCCESS (0) on success with 'result' filled out
|
||||
// error code otherwise, as defined by the PARSE_* macros
|
||||
int parseFrom(EXIFStream& stream);
|
||||
int parseFrom(const uint8_t* data, unsigned length);
|
||||
int parseFrom(const std::string& data);
|
||||
|
||||
// Parsing function for an EXIF segment. This is used internally by parseFrom()
|
||||
// but can be called for special cases where only the EXIF section is
|
||||
|
||||
204
main.cpp
204
main.cpp
@@ -9,108 +9,130 @@
|
||||
#include <vector> // std::vector
|
||||
#include <iomanip> // std::setprecision
|
||||
|
||||
class EXIFStreamFile : public TinyEXIF::EXIFStream {
|
||||
public:
|
||||
explicit EXIFStreamFile(const char* fileName)
|
||||
: file(fileName, std::ifstream::in|std::ifstream::binary) {}
|
||||
bool IsValid() const override {
|
||||
return file.is_open();
|
||||
}
|
||||
const uint8_t* GetBuffer(unsigned desiredLength) override {
|
||||
buffer.resize(desiredLength);
|
||||
if (!file.read((char*)buffer.data(), desiredLength))
|
||||
return NULL;
|
||||
return buffer.data();
|
||||
}
|
||||
bool SkipBuffer(unsigned desiredLength) override {
|
||||
return (bool)file.seekg(desiredLength,std::ios::cur);
|
||||
}
|
||||
private:
|
||||
std::ifstream file;
|
||||
std::vector<uint8_t> buffer;
|
||||
};
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
std::cout << "Usage: TinyEXIF <image_file>" << "\n";
|
||||
std::cout << "Usage: TinyEXIF <image_file>\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// read entire image file
|
||||
std::ifstream file(argv[1], std::ifstream::in|std::ifstream::binary);
|
||||
if (!file.is_open()) {
|
||||
EXIFStreamFile stream(argv[1]);
|
||||
if (!stream.IsValid()) {
|
||||
std::cout << "error: can not open '" << argv[1] << "'\n";
|
||||
return -2;
|
||||
}
|
||||
file.seekg(0,std::ios::end);
|
||||
std::streampos length = file.tellg();
|
||||
file.seekg(0,std::ios::beg);
|
||||
std::vector<uint8_t> data(length);
|
||||
file.read((char*)data.data(), length);
|
||||
|
||||
// parse image EXIF and XMP metadata
|
||||
TinyEXIF::EXIFInfo imageEXIF(data.data(), (unsigned)length);
|
||||
if (imageEXIF.Fields) {
|
||||
if (imageEXIF.ImageWidth || imageEXIF.ImageHeight)
|
||||
std::cout << "ImageResolution " << imageEXIF.ImageWidth << "x" << imageEXIF.ImageHeight << " pixels" << "\n";
|
||||
if (imageEXIF.RelatedImageWidth || imageEXIF.RelatedImageHeight)
|
||||
std::cout << "RelatedImageResolution " << imageEXIF.RelatedImageWidth << "x" << imageEXIF.RelatedImageHeight << " pixels" << "\n";
|
||||
if (!imageEXIF.ImageDescription.empty())
|
||||
std::cout << "Description " << imageEXIF.ImageDescription << "\n";
|
||||
if (!imageEXIF.Make.empty() || !imageEXIF.Model.empty())
|
||||
std::cout << "CameraModel " << imageEXIF.Make << " - " << imageEXIF.Model << "\n";
|
||||
if (!imageEXIF.SerialNumber.empty())
|
||||
std::cout << "SerialNumber " << imageEXIF.SerialNumber << "\n";
|
||||
std::cout << "Orientation " << imageEXIF.Orientation << "\n";
|
||||
std::cout << "Resolution " << imageEXIF.XResolution << "x" << imageEXIF.YResolution << "\n";
|
||||
std::cout << "ResolutionUnit " << imageEXIF.ResolutionUnit << "\n";
|
||||
std::cout << "BitsPerSample " << imageEXIF.BitsPerSample << "\n";
|
||||
if (!imageEXIF.Software.empty())
|
||||
std::cout << "Software " << imageEXIF.Software << "\n";
|
||||
if (!imageEXIF.DateTime.empty())
|
||||
std::cout << "DateTime " << imageEXIF.DateTime << "\n";
|
||||
if (!imageEXIF.DateTimeOriginal.empty())
|
||||
std::cout << "DateTimeOriginal " << imageEXIF.DateTimeOriginal << "\n";
|
||||
if (!imageEXIF.DateTimeDigitized.empty())
|
||||
std::cout << "DateTimeDigitized " << imageEXIF.DateTimeDigitized << "\n";
|
||||
if (!imageEXIF.SubSecTimeOriginal.empty())
|
||||
std::cout << "SubSecTimeOriginal " << imageEXIF.SubSecTimeOriginal << "\n";
|
||||
if (!imageEXIF.Copyright.empty())
|
||||
std::cout << "Copyright " << imageEXIF.Copyright << "\n";
|
||||
std::cout << "ExposureTime " << std::setprecision(10) << imageEXIF.ExposureTime << " s" << "\n";
|
||||
std::cout << "FNumber " << imageEXIF.FNumber << "\n";
|
||||
std::cout << "ExposureProgram " << imageEXIF.ExposureProgram << "\n";
|
||||
std::cout << "ISOSpeed " << imageEXIF.ISOSpeedRatings << "\n";
|
||||
std::cout << "ShutterSpeedValue " << std::setprecision(10) << imageEXIF.ShutterSpeedValue << "\n";
|
||||
std::cout << "ApertureValue " << std::setprecision(10) << imageEXIF.ApertureValue << "\n";
|
||||
std::cout << "BrightnessValue " << std::setprecision(10) << imageEXIF.BrightnessValue << "\n";
|
||||
std::cout << "ExposureBiasValue " << imageEXIF.ExposureBiasValue << "\n";
|
||||
std::cout << "SubjectDistance " << imageEXIF.SubjectDistance << "\n";
|
||||
std::cout << "FocalLength " << imageEXIF.FocalLength << " mm" << "\n";
|
||||
std::cout << "Flash " << imageEXIF.Flash << "\n";
|
||||
if (!imageEXIF.SubjectArea.empty()) {
|
||||
std::cout << "SubjectArea";
|
||||
for (uint16_t val: imageEXIF.SubjectArea)
|
||||
std::cout << " " << val;
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << "MeteringMode " << imageEXIF.MeteringMode << "\n";
|
||||
std::cout << "LightSource " << imageEXIF.LightSource << "\n";
|
||||
std::cout << "ProjectionType " << imageEXIF.ProjectionType << "\n";
|
||||
std::cout << "LensInfo.FStopMin " << imageEXIF.LensInfo.FStopMin << "\n";
|
||||
std::cout << "LensInfo.FStopMax " << imageEXIF.LensInfo.FStopMax << "\n";
|
||||
std::cout << "LensInfo.FocalLengthMin " << imageEXIF.LensInfo.FocalLengthMin << " mm" << "\n";
|
||||
std::cout << "LensInfo.FocalLengthMax " << imageEXIF.LensInfo.FocalLengthMax << " mm" << "\n";
|
||||
std::cout << "LensInfo.DigitalZoomRatio " << imageEXIF.LensInfo.DigitalZoomRatio << "\n";
|
||||
std::cout << "LensInfo.FocalLengthIn35mm " << imageEXIF.LensInfo.FocalLengthIn35mm << "\n";
|
||||
std::cout << "LensInfo.FocalPlaneXResolution " << std::setprecision(10) << imageEXIF.LensInfo.FocalPlaneXResolution << "\n";
|
||||
std::cout << "LensInfo.FocalPlaneYResolution " << std::setprecision(10) << imageEXIF.LensInfo.FocalPlaneYResolution << "\n";
|
||||
std::cout << "LensInfo.FocalPlaneResolutionUnit " << imageEXIF.LensInfo.FocalPlaneResolutionUnit << "\n";
|
||||
if (!imageEXIF.LensInfo.Make.empty() || !imageEXIF.LensInfo.Model.empty())
|
||||
std::cout << "LensInfo.Model " << imageEXIF.LensInfo.Make << " - " << imageEXIF.LensInfo.Model << "\n";
|
||||
if (imageEXIF.GeoLocation.hasLatLon()) {
|
||||
std::cout << "GeoLocation.Latitude " << std::setprecision(10) << imageEXIF.GeoLocation.Latitude << "\n";
|
||||
std::cout << "GeoLocation.Longitude " << std::setprecision(10) << imageEXIF.GeoLocation.Longitude << "\n";
|
||||
}
|
||||
if (imageEXIF.GeoLocation.hasAltitude()) {
|
||||
std::cout << "GeoLocation.Altitude " << imageEXIF.GeoLocation.Altitude << " m" << "\n";
|
||||
std::cout << "GeoLocation.AltitudeRef " << (int)imageEXIF.GeoLocation.AltitudeRef << "\n";
|
||||
}
|
||||
if (imageEXIF.GeoLocation.hasRelativeAltitude())
|
||||
std::cout << "GeoLocation.RelativeAltitude " << imageEXIF.GeoLocation.RelativeAltitude << "\n";
|
||||
if (imageEXIF.GeoLocation.hasOrientation()) {
|
||||
std::cout << "GeoLocation.RollDegree " << imageEXIF.GeoLocation.RollDegree << "\n";
|
||||
std::cout << "GeoLocation.PitchDegree " << imageEXIF.GeoLocation.PitchDegree << "\n";
|
||||
std::cout << "GeoLocation.YawDegree " << imageEXIF.GeoLocation.YawDegree << "\n";
|
||||
}
|
||||
std::cout << "GeoLocation.GPSDOP " << imageEXIF.GeoLocation.GPSDOP << "\n";
|
||||
std::cout << "GeoLocation.GPSDifferential " << imageEXIF.GeoLocation.GPSDifferential << "\n";
|
||||
if (!imageEXIF.GeoLocation.GPSMapDatum.empty())
|
||||
std::cout << "GeoLocation.GPSMapDatum " << imageEXIF.GeoLocation.GPSMapDatum << "\n";
|
||||
if (!imageEXIF.GeoLocation.GPSTimeStamp.empty())
|
||||
std::cout << "GeoLocation.GPSTimeStamp " << imageEXIF.GeoLocation.GPSTimeStamp << "\n";
|
||||
if (!imageEXIF.GeoLocation.GPSDateStamp.empty())
|
||||
std::cout << "GeoLocation.GPSDateStamp " << imageEXIF.GeoLocation.GPSDateStamp << "\n";
|
||||
TinyEXIF::EXIFInfo imageEXIF(stream);
|
||||
if (!imageEXIF.Fields) {
|
||||
std::cout << "error: no EXIF or XMP metadata\n";
|
||||
return -3;
|
||||
}
|
||||
return 0;
|
||||
|
||||
// print extracted metadata
|
||||
if (imageEXIF.ImageWidth || imageEXIF.ImageHeight)
|
||||
std::cout << "ImageResolution " << imageEXIF.ImageWidth << "x" << imageEXIF.ImageHeight << " pixels" << "\n";
|
||||
if (imageEXIF.RelatedImageWidth || imageEXIF.RelatedImageHeight)
|
||||
std::cout << "RelatedImageResolution " << imageEXIF.RelatedImageWidth << "x" << imageEXIF.RelatedImageHeight << " pixels" << "\n";
|
||||
if (!imageEXIF.ImageDescription.empty())
|
||||
std::cout << "Description " << imageEXIF.ImageDescription << "\n";
|
||||
if (!imageEXIF.Make.empty() || !imageEXIF.Model.empty())
|
||||
std::cout << "CameraModel " << imageEXIF.Make << " - " << imageEXIF.Model << "\n";
|
||||
if (!imageEXIF.SerialNumber.empty())
|
||||
std::cout << "SerialNumber " << imageEXIF.SerialNumber << "\n";
|
||||
if (imageEXIF.Orientation)
|
||||
std::cout << "Orientation " << imageEXIF.Orientation << "\n";
|
||||
if (imageEXIF.XResolution || imageEXIF.YResolution || imageEXIF.ResolutionUnit)
|
||||
std::cout << "Resolution " << imageEXIF.XResolution << "x" << imageEXIF.YResolution << " (" << imageEXIF.ResolutionUnit << ")\n";
|
||||
if (imageEXIF.BitsPerSample)
|
||||
std::cout << "BitsPerSample " << imageEXIF.BitsPerSample << "\n";
|
||||
if (!imageEXIF.Software.empty())
|
||||
std::cout << "Software " << imageEXIF.Software << "\n";
|
||||
if (!imageEXIF.DateTime.empty())
|
||||
std::cout << "DateTime " << imageEXIF.DateTime << "\n";
|
||||
if (!imageEXIF.DateTimeOriginal.empty())
|
||||
std::cout << "DateTimeOriginal " << imageEXIF.DateTimeOriginal << "\n";
|
||||
if (!imageEXIF.DateTimeDigitized.empty())
|
||||
std::cout << "DateTimeDigitized " << imageEXIF.DateTimeDigitized << "\n";
|
||||
if (!imageEXIF.SubSecTimeOriginal.empty())
|
||||
std::cout << "SubSecTimeOriginal " << imageEXIF.SubSecTimeOriginal << "\n";
|
||||
if (!imageEXIF.Copyright.empty())
|
||||
std::cout << "Copyright " << imageEXIF.Copyright << "\n";
|
||||
std::cout << "ExposureTime " << std::setprecision(10) << imageEXIF.ExposureTime << " s" << "\n";
|
||||
std::cout << "FNumber " << imageEXIF.FNumber << "\n";
|
||||
std::cout << "ExposureProgram " << imageEXIF.ExposureProgram << "\n";
|
||||
std::cout << "ISOSpeed " << imageEXIF.ISOSpeedRatings << "\n";
|
||||
std::cout << "ShutterSpeedValue " << std::setprecision(10) << imageEXIF.ShutterSpeedValue << "\n";
|
||||
std::cout << "ApertureValue " << std::setprecision(10) << imageEXIF.ApertureValue << "\n";
|
||||
std::cout << "BrightnessValue " << std::setprecision(10) << imageEXIF.BrightnessValue << "\n";
|
||||
std::cout << "ExposureBiasValue " << imageEXIF.ExposureBiasValue << "\n";
|
||||
std::cout << "SubjectDistance " << imageEXIF.SubjectDistance << "\n";
|
||||
std::cout << "FocalLength " << imageEXIF.FocalLength << " mm" << "\n";
|
||||
std::cout << "Flash " << imageEXIF.Flash << "\n";
|
||||
if (!imageEXIF.SubjectArea.empty()) {
|
||||
std::cout << "SubjectArea";
|
||||
for (uint16_t val: imageEXIF.SubjectArea)
|
||||
std::cout << " " << val;
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << "MeteringMode " << imageEXIF.MeteringMode << "\n";
|
||||
std::cout << "LightSource " << imageEXIF.LightSource << "\n";
|
||||
std::cout << "ProjectionType " << imageEXIF.ProjectionType << "\n";
|
||||
std::cout << "LensInfo.FStopMin " << imageEXIF.LensInfo.FStopMin << "\n";
|
||||
std::cout << "LensInfo.FStopMax " << imageEXIF.LensInfo.FStopMax << "\n";
|
||||
std::cout << "LensInfo.FocalLengthMin " << imageEXIF.LensInfo.FocalLengthMin << " mm" << "\n";
|
||||
std::cout << "LensInfo.FocalLengthMax " << imageEXIF.LensInfo.FocalLengthMax << " mm" << "\n";
|
||||
std::cout << "LensInfo.DigitalZoomRatio " << imageEXIF.LensInfo.DigitalZoomRatio << "\n";
|
||||
std::cout << "LensInfo.FocalLengthIn35mm " << imageEXIF.LensInfo.FocalLengthIn35mm << "\n";
|
||||
std::cout << "LensInfo.FocalPlaneXResolution " << std::setprecision(10) << imageEXIF.LensInfo.FocalPlaneXResolution << "\n";
|
||||
std::cout << "LensInfo.FocalPlaneYResolution " << std::setprecision(10) << imageEXIF.LensInfo.FocalPlaneYResolution << "\n";
|
||||
std::cout << "LensInfo.FocalPlaneResolutionUnit " << imageEXIF.LensInfo.FocalPlaneResolutionUnit << "\n";
|
||||
if (!imageEXIF.LensInfo.Make.empty() || !imageEXIF.LensInfo.Model.empty())
|
||||
std::cout << "LensInfo.Model " << imageEXIF.LensInfo.Make << " - " << imageEXIF.LensInfo.Model << "\n";
|
||||
if (imageEXIF.GeoLocation.hasLatLon()) {
|
||||
std::cout << "GeoLocation.Latitude " << std::setprecision(10) << imageEXIF.GeoLocation.Latitude << "\n";
|
||||
std::cout << "GeoLocation.Longitude " << std::setprecision(10) << imageEXIF.GeoLocation.Longitude << "\n";
|
||||
}
|
||||
if (imageEXIF.GeoLocation.hasAltitude()) {
|
||||
std::cout << "GeoLocation.Altitude " << imageEXIF.GeoLocation.Altitude << " m" << "\n";
|
||||
std::cout << "GeoLocation.AltitudeRef " << (int)imageEXIF.GeoLocation.AltitudeRef << "\n";
|
||||
}
|
||||
if (imageEXIF.GeoLocation.hasRelativeAltitude())
|
||||
std::cout << "GeoLocation.RelativeAltitude " << imageEXIF.GeoLocation.RelativeAltitude << "\n";
|
||||
if (imageEXIF.GeoLocation.hasOrientation()) {
|
||||
std::cout << "GeoLocation.RollDegree " << imageEXIF.GeoLocation.RollDegree << "\n";
|
||||
std::cout << "GeoLocation.PitchDegree " << imageEXIF.GeoLocation.PitchDegree << "\n";
|
||||
std::cout << "GeoLocation.YawDegree " << imageEXIF.GeoLocation.YawDegree << "\n";
|
||||
}
|
||||
std::cout << "GeoLocation.GPSDOP " << imageEXIF.GeoLocation.GPSDOP << "\n";
|
||||
std::cout << "GeoLocation.GPSDifferential " << imageEXIF.GeoLocation.GPSDifferential << "\n";
|
||||
if (!imageEXIF.GeoLocation.GPSMapDatum.empty())
|
||||
std::cout << "GeoLocation.GPSMapDatum " << imageEXIF.GeoLocation.GPSMapDatum << "\n";
|
||||
if (!imageEXIF.GeoLocation.GPSTimeStamp.empty())
|
||||
std::cout << "GeoLocation.GPSTimeStamp " << imageEXIF.GeoLocation.GPSTimeStamp << "\n";
|
||||
if (!imageEXIF.GeoLocation.GPSDateStamp.empty())
|
||||
std::cout << "GeoLocation.GPSDateStamp " << imageEXIF.GeoLocation.GPSDateStamp << "\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user