feat: use libexiv2 to obtain more image metadata
This commit is contained in:
parent
8c152dc862
commit
5705f02636
2
.github/workflows/ubuntu.yml
vendored
2
.github/workflows/ubuntu.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Get build dept.
|
||||
run: sudo apt install cmake qtbase5-dev libqt5svg5-dev qttools5-dev
|
||||
run: sudo apt install cmake extra-cmake-modules qtbase5-dev libqt5svg5-dev qttools5-dev libexiv2-dev
|
||||
- name: Build it
|
||||
run: |
|
||||
mkdir build
|
||||
|
|
|
@ -9,8 +9,21 @@ set (CMAKE_AUTOMOC ON)
|
|||
set (CMAKE_AUTORCC ON)
|
||||
set (QT_MINIMUM_VERSION "5.10")
|
||||
|
||||
option (EXIV2_METADATA_SUPPORT "Better image metadata support via libexiv2" ON)
|
||||
|
||||
find_package(Qt5 ${QT_MINIMUM_VERSION} CONFIG REQUIRED Widgets Svg LinguistTools)
|
||||
|
||||
if (EXIV2_METADATA_SUPPORT)
|
||||
find_package(Exiv2)
|
||||
set_package_properties(Exiv2 PROPERTIES
|
||||
URL "https://www.exiv2.org"
|
||||
DESCRIPTION "image metadata support"
|
||||
TYPE OPTIONAL
|
||||
PURPOSE "Bring better image metadata support"
|
||||
)
|
||||
endif ()
|
||||
|
||||
#LibExiv2_FOUND
|
||||
set (PPIC_CPP_FILES
|
||||
app/main.cpp
|
||||
app/mainwindow.cpp
|
||||
|
@ -25,6 +38,7 @@ set (PPIC_CPP_FILES
|
|||
app/aboutdialog.cpp
|
||||
app/metadatamodel.cpp
|
||||
app/metadatadialog.cpp
|
||||
app/exiv2wrapper.cpp
|
||||
)
|
||||
|
||||
set (PPIC_HEADER_FILES
|
||||
|
@ -40,6 +54,7 @@ set (PPIC_HEADER_FILES
|
|||
app/aboutdialog.h
|
||||
app/metadatamodel.h
|
||||
app/metadatadialog.h
|
||||
app/exiv2wrapper.h
|
||||
)
|
||||
|
||||
set (PPIC_QRC_FILES
|
||||
|
@ -72,6 +87,20 @@ add_executable (${EXE_NAME}
|
|||
|
||||
target_link_libraries (${EXE_NAME} Qt5::Widgets Qt5::Svg)
|
||||
|
||||
if (Exiv2_FOUND)
|
||||
message(INFO ${LibExiv2_INCLUDE_DIRS})
|
||||
target_include_directories(${EXE_NAME}
|
||||
PRIVATE
|
||||
${LibExiv2_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries (${EXE_NAME}
|
||||
Exiv2
|
||||
)
|
||||
target_compile_definitions(${EXE_NAME} PRIVATE
|
||||
HAVE_EXIV2_VERSION="${LibExiv2_VERSION}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Extra build settings
|
||||
if (WIN32)
|
||||
set_property (
|
||||
|
@ -194,6 +223,8 @@ install (
|
|||
DESTINATION ${QM_FILE_INSTALL_DIR}
|
||||
)
|
||||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
# CPACK: General Settings
|
||||
set (CPACK_GENERATOR "TBZ2")
|
||||
set (CPACK_PACKAGE_NAME "pineapple-pictures")
|
||||
|
@ -206,7 +237,7 @@ elseif (APPLE)
|
|||
# ...
|
||||
elseif (UNIX)
|
||||
set (CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
|
||||
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5svg5")
|
||||
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5svg5 libexiv2-14")
|
||||
set (CPACK_DEBIAN_PACKAGE_RECOMMENDS "kimageformat-plugins")
|
||||
endif()
|
||||
|
||||
|
|
|
@ -109,6 +109,9 @@ SOFTWARE.
|
|||
QStringLiteral("<h1 align='center'><b>%1</b></h1>").arg(tr("Third-party Libraries used by %1")),
|
||||
tr("%1 is built on the following free software libraries:"),
|
||||
QStringLiteral("<ul>"),
|
||||
#ifdef HAVE_EXIV2_VERSION
|
||||
QStringLiteral("<li><a href='%1'>%2</a>: %3</li>").arg("https://www.exiv2.org/", "Exiv2", "GPLv2"),
|
||||
#endif // EXIV2_VERSION
|
||||
QStringLiteral("<li><a href='%1'>%2</a>: %3</li>").arg("https://www.qt.io/", "Qt", "GPLv2 + GPLv3 + LGPLv2.1 + LGPLv3"),
|
||||
QStringLiteral("</ul>")
|
||||
};
|
||||
|
|
100
app/exiv2wrapper.cpp
Normal file
100
app/exiv2wrapper.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "exiv2wrapper.h"
|
||||
|
||||
#ifdef HAVE_EXIV2_VERSION
|
||||
#include <exiv2/exiv2.hpp>
|
||||
#else // HAVE_EXIV2_VERSION
|
||||
namespace Exiv2 {
|
||||
class Image {};
|
||||
}
|
||||
#endif // HAVE_EXIV2_VERSION
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
Exiv2Wrapper::Exiv2Wrapper()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Exiv2Wrapper::~Exiv2Wrapper()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template<typename Collection, typename Iterator>
|
||||
void Exiv2Wrapper::cacheSection(Collection collection)
|
||||
{
|
||||
const Collection& exifData = collection;
|
||||
Iterator it = exifData.begin(), end = exifData.end();
|
||||
for (; it != end; ++it) {
|
||||
QString key = QString::fromUtf8(it->key().c_str());
|
||||
QString label = QString::fromLocal8Bit(it->tagLabel().c_str());
|
||||
std::ostringstream stream;
|
||||
stream << *it;
|
||||
QString value = QString::fromLocal8Bit(stream.str().c_str());
|
||||
m_metadataValue.insert(key, value);
|
||||
m_metadataLabel.insert(key, label);
|
||||
|
||||
qDebug() << key << label << value;
|
||||
}
|
||||
}
|
||||
|
||||
bool Exiv2Wrapper::load(const QString &filePath)
|
||||
{
|
||||
#ifdef HAVE_EXIV2_VERSION
|
||||
QByteArray filePathByteArray = QFile::encodeName(filePath);
|
||||
try {
|
||||
m_exivImage.reset(Exiv2::ImageFactory::open(filePathByteArray.constData()).release());
|
||||
m_exivImage->readMetadata();
|
||||
} catch (const Exiv2::Error& error) {
|
||||
m_errMsg = QString::fromUtf8(error.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else // HAVE_EXIV2_VERSION
|
||||
Q_UNUSED(filePath);
|
||||
return false;
|
||||
#endif // HAVE_EXIV2_VERSION
|
||||
}
|
||||
|
||||
void Exiv2Wrapper::cacheSections()
|
||||
{
|
||||
#ifdef HAVE_EXIV2_VERSION
|
||||
if (m_exivImage->checkMode(Exiv2::mdExif) & Exiv2::amRead) {
|
||||
cacheSection<Exiv2::ExifData, Exiv2::ExifData::const_iterator>(m_exivImage->exifData());
|
||||
}
|
||||
|
||||
if (m_exivImage->checkMode(Exiv2::mdIptc) & Exiv2::amRead) {
|
||||
cacheSection<Exiv2::IptcData, Exiv2::IptcData::const_iterator>(m_exivImage->iptcData());
|
||||
}
|
||||
|
||||
if (m_exivImage->checkMode(Exiv2::mdXmp) & Exiv2::amRead) {
|
||||
cacheSection<Exiv2::XmpData, Exiv2::XmpData::const_iterator>(m_exivImage->xmpData());
|
||||
}
|
||||
|
||||
// qDebug() << m_metadataValue;
|
||||
// qDebug() << m_metadataLabel;
|
||||
#endif // HAVE_EXIV2_VERSION
|
||||
}
|
||||
|
||||
QString Exiv2Wrapper::comment() const
|
||||
{
|
||||
#ifdef HAVE_EXIV2_VERSION
|
||||
return m_exivImage->comment().c_str();
|
||||
#else // HAVE_EXIV2_VERSION
|
||||
return QString();
|
||||
#endif // HAVE_EXIV2_VERSION
|
||||
}
|
||||
|
||||
QString Exiv2Wrapper::label(const QString &key) const
|
||||
{
|
||||
return m_metadataLabel.value(key);
|
||||
}
|
||||
|
||||
QString Exiv2Wrapper::value(const QString &key) const
|
||||
{
|
||||
return m_metadataValue.value(key);
|
||||
}
|
||||
|
36
app/exiv2wrapper.h
Normal file
36
app/exiv2wrapper.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef EXIV2WRAPPER_H
|
||||
#define EXIV2WRAPPER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
namespace Exiv2 {
|
||||
class Image;
|
||||
}
|
||||
|
||||
class Exiv2Wrapper
|
||||
{
|
||||
public:
|
||||
Exiv2Wrapper();
|
||||
~Exiv2Wrapper();
|
||||
|
||||
bool load(const QString& filePath);
|
||||
void cacheSections();
|
||||
|
||||
QString comment() const;
|
||||
QString label(const QString & key) const;
|
||||
QString value(const QString & key) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Exiv2::Image> m_exivImage;
|
||||
QMap<QString, QString> m_metadataValue;
|
||||
QMap<QString, QString> m_metadataLabel;
|
||||
QString m_errMsg;
|
||||
|
||||
template<typename Collection, typename Iterator>
|
||||
void cacheSection(Collection collection);
|
||||
};
|
||||
|
||||
#endif // EXIV2WRAPPER_H
|
|
@ -1,4 +1,5 @@
|
|||
#include "metadatamodel.h"
|
||||
#include "exiv2wrapper.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
|
@ -31,14 +32,12 @@ void MetadataModel::setFile(const QString &imageFilePath)
|
|||
const QString & imageDimensionsString = imageSize(imgReader.size());
|
||||
const QString & imageRatioString = imageSizeRatio(imgReader.size());
|
||||
|
||||
#if 0
|
||||
appendSection(QStringLiteral("Description"), tr("Description", "Section name."));
|
||||
appendSection(QStringLiteral("Origin"), tr("Origin", "Section name."));
|
||||
#endif // 0
|
||||
appendSection(QStringLiteral("Image"), tr("Image", "Section name."));
|
||||
#if 0
|
||||
appendSection(QStringLiteral("Camera"), tr("Camera", "Section name."));
|
||||
appendSection(QStringLiteral("AdvancedPhoto"), tr("Advanced photo", "Section name."));
|
||||
#if 0
|
||||
appendSection(QStringLiteral("GPS"), tr("GPS", "Section name."));
|
||||
#endif // 0
|
||||
appendSection(QStringLiteral("File"), tr("File", "Section name."));
|
||||
|
@ -60,6 +59,31 @@ void MetadataModel::setFile(const QString &imageFilePath)
|
|||
tr("Date Created"), birthTimeString);
|
||||
appendProperty(QStringLiteral("File"), QStringLiteral("File.LastModified"),
|
||||
tr("Date Modified"), lastModifiedTimeString);
|
||||
|
||||
Exiv2Wrapper wrapper;
|
||||
if (wrapper.load(imageFilePath)) {
|
||||
wrapper.cacheSections();
|
||||
|
||||
appendProperty(QStringLiteral("Description"), QStringLiteral("Description.Comments"),
|
||||
tr("Comments"), wrapper.comment());
|
||||
|
||||
appendExivPropertyIfExist(wrapper, QStringLiteral("Origin"),
|
||||
QStringLiteral("Exif.Image.Software"), tr("Program name"));
|
||||
appendExivPropertyIfExist(wrapper, QStringLiteral("Image"),
|
||||
QStringLiteral("Exif.Photo.ColorSpace"), tr("Colour representation"));
|
||||
appendExivPropertyIfExist(wrapper, QStringLiteral("Camera"),
|
||||
QStringLiteral("Exif.Image.Make"), tr("Camera maker"));
|
||||
appendExivPropertyIfExist(wrapper, QStringLiteral("Camera"),
|
||||
QStringLiteral("Exif.Image.Model"), tr("Camera model"));
|
||||
appendExivPropertyIfExist(wrapper, QStringLiteral("Camera"),
|
||||
QStringLiteral("Exif.Photo.ISOSpeedRatings"), tr("ISO speed"));
|
||||
appendExivPropertyIfExist(wrapper, QStringLiteral("Camera"),
|
||||
QStringLiteral("Exif.Photo.FocalLength"), tr("Focal length"));
|
||||
appendExivPropertyIfExist(wrapper, QStringLiteral("AdvancedPhoto"),
|
||||
QStringLiteral("Exif.Photo.DigitalZoomRatio"), tr("Digital zoom"));
|
||||
appendExivPropertyIfExist(wrapper, QStringLiteral("AdvancedPhoto"),
|
||||
QStringLiteral("Exif.Photo.ExifVersion"), tr("EXIF version"));
|
||||
}
|
||||
}
|
||||
|
||||
QString MetadataModel::imageSize(const QSize &size)
|
||||
|
@ -126,6 +150,18 @@ bool MetadataModel::updateProperty(const QString &propertyKey, const QString &pr
|
|||
return false;
|
||||
}
|
||||
|
||||
bool MetadataModel::appendExivPropertyIfExist(const Exiv2Wrapper &wrapper, const QString §ionKey, const QString &exiv2propertyKey, const QString &propertyDisplayName)
|
||||
{
|
||||
const QString & value = wrapper.value(exiv2propertyKey);
|
||||
if (!value.isEmpty()) {
|
||||
appendProperty(sectionKey, exiv2propertyKey,
|
||||
propertyDisplayName.isEmpty() ? wrapper.label(exiv2propertyKey) : propertyDisplayName,
|
||||
value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex MetadataModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent)) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
class Exiv2Wrapper;
|
||||
class MetadataModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -18,6 +19,8 @@ public:
|
|||
bool appendProperty(const QString & sectionKey, const QString & propertyKey,
|
||||
const QString & propertyDisplayName, const QString & propertyValue = QString());
|
||||
bool updateProperty(const QString & propertyKey, const QString & propertyValue);
|
||||
bool appendExivPropertyIfExist(const Exiv2Wrapper & wrapper, const QString & sectionKey,
|
||||
const QString & exiv2propertyKey, const QString & propertyDisplayName = QString());
|
||||
|
||||
private:
|
||||
enum RowType : quintptr {
|
||||
|
|
|
@ -29,7 +29,8 @@ SOURCES += \
|
|||
app/settings.cpp \
|
||||
app/settingsdialog.cpp \
|
||||
app/metadatamodel.cpp \
|
||||
app/metadatadialog.cpp
|
||||
app/metadatadialog.cpp \
|
||||
app/exiv2wrapper.cpp
|
||||
|
||||
HEADERS += \
|
||||
app/aboutdialog.h \
|
||||
|
@ -43,7 +44,8 @@ HEADERS += \
|
|||
app/settings.h \
|
||||
app/settingsdialog.h \
|
||||
app/metadatamodel.h \
|
||||
app/metadatadialog.h
|
||||
app/metadatadialog.h \
|
||||
app/exiv2wrapper.h
|
||||
|
||||
TRANSLATIONS = \
|
||||
translations/PineapplePictures.ts \
|
||||
|
|
Loading…
Reference in New Issue
Block a user