diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index e5e05e2..8de297a 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 288b02e..66a81dc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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()
diff --git a/app/aboutdialog.cpp b/app/aboutdialog.cpp
index df4e0da..54eba01 100644
--- a/app/aboutdialog.cpp
+++ b/app/aboutdialog.cpp
@@ -109,6 +109,9 @@ SOFTWARE.
QStringLiteral("
%1
").arg(tr("Third-party Libraries used by %1")),
tr("%1 is built on the following free software libraries:"),
QStringLiteral(""),
+#ifdef HAVE_EXIV2_VERSION
+ QStringLiteral("- %2: %3
").arg("https://www.exiv2.org/", "Exiv2", "GPLv2"),
+#endif // EXIV2_VERSION
QStringLiteral("- %2: %3
").arg("https://www.qt.io/", "Qt", "GPLv2 + GPLv3 + LGPLv2.1 + LGPLv3"),
QStringLiteral("
")
};
diff --git a/app/exiv2wrapper.cpp b/app/exiv2wrapper.cpp
new file mode 100644
index 0000000..1ec5d82
--- /dev/null
+++ b/app/exiv2wrapper.cpp
@@ -0,0 +1,100 @@
+#include "exiv2wrapper.h"
+
+#ifdef HAVE_EXIV2_VERSION
+#include
+#else // HAVE_EXIV2_VERSION
+namespace Exiv2 {
+class Image {};
+}
+#endif // HAVE_EXIV2_VERSION
+
+#include
+
+#include
+#include
+
+Exiv2Wrapper::Exiv2Wrapper()
+{
+
+}
+
+Exiv2Wrapper::~Exiv2Wrapper()
+{
+
+}
+
+template
+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(m_exivImage->exifData());
+ }
+
+ if (m_exivImage->checkMode(Exiv2::mdIptc) & Exiv2::amRead) {
+ cacheSection(m_exivImage->iptcData());
+ }
+
+ if (m_exivImage->checkMode(Exiv2::mdXmp) & Exiv2::amRead) {
+ cacheSection(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);
+}
+
diff --git a/app/exiv2wrapper.h b/app/exiv2wrapper.h
new file mode 100644
index 0000000..e2d050b
--- /dev/null
+++ b/app/exiv2wrapper.h
@@ -0,0 +1,36 @@
+#ifndef EXIV2WRAPPER_H
+#define EXIV2WRAPPER_H
+
+#include
+
+#include
+#include
+
+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 m_exivImage;
+ QMap m_metadataValue;
+ QMap m_metadataLabel;
+ QString m_errMsg;
+
+ template
+ void cacheSection(Collection collection);
+};
+
+#endif // EXIV2WRAPPER_H
diff --git a/app/metadatamodel.cpp b/app/metadatamodel.cpp
index ecacc62..640ee0a 100644
--- a/app/metadatamodel.cpp
+++ b/app/metadatamodel.cpp
@@ -1,4 +1,5 @@
#include "metadatamodel.h"
+#include "exiv2wrapper.h"
#include
#include
@@ -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)) {
diff --git a/app/metadatamodel.h b/app/metadatamodel.h
index babfe70..9112d39 100644
--- a/app/metadatamodel.h
+++ b/app/metadatamodel.h
@@ -3,6 +3,7 @@
#include
+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 {
diff --git a/pineapple-pictures.pro b/pineapple-pictures.pro
index 35e9f21..724fd00 100644
--- a/pineapple-pictures.pro
+++ b/pineapple-pictures.pro
@@ -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 \