git repos / SagoImageBrowser

blame: src/exifreader.cpp

normal view · raw

53fdab93 Poul Sander 2026-05-18 18:45 1
#include "exifreader.h"
53fdab93 Poul Sander 2026-05-18 18:45 2
53fdab93 Poul Sander 2026-05-18 18:45 3
#include <QFileInfo>
53fdab93 Poul Sander 2026-05-18 18:45 4
#include <QImageReader>
53fdab93 Poul Sander 2026-05-18 18:45 5
53fdab93 Poul Sander 2026-05-18 18:45 6
#include <exiv2/exiv2.hpp>
53fdab93 Poul Sander 2026-05-18 18:45 7
0eae7b4d Poul Sander 2026-05-18 19:22 8
// Raw EXIF date format is "YYYY:MM:DD HH:MM:SS"; reformat date separators to dashes
0eae7b4d Poul Sander 2026-05-18 19:22 9
static QString formatDate(const QString &raw)
0eae7b4d Poul Sander 2026-05-18 19:22 10
{
0eae7b4d Poul Sander 2026-05-18 19:22 11
    if (raw.size() >= 10 && raw[4] == u':' && raw[7] == u':')
0eae7b4d Poul Sander 2026-05-18 19:22 12
        return raw.left(4) + u'-' + raw.mid(5, 2) + u'-' + raw.mid(8, 2) + raw.mid(10);
0eae7b4d Poul Sander 2026-05-18 19:22 13
    return raw;
0eae7b4d Poul Sander 2026-05-18 19:22 14
}
0eae7b4d Poul Sander 2026-05-18 19:22 15
0eae7b4d Poul Sander 2026-05-18 19:22 16
static QString findTag(const Exiv2::ExifData &exif, const std::string &key)
0eae7b4d Poul Sander 2026-05-18 19:22 17
{
0eae7b4d Poul Sander 2026-05-18 19:22 18
    try {
0eae7b4d Poul Sander 2026-05-18 19:22 19
        Exiv2::ExifData::const_iterator it = exif.findKey(Exiv2::ExifKey(key));
0eae7b4d Poul Sander 2026-05-18 19:22 20
        if (it != exif.end())
0eae7b4d Poul Sander 2026-05-18 19:22 21
            return QString::fromStdString(it->print(&exif)).trimmed();
0eae7b4d Poul Sander 2026-05-18 19:22 22
    } catch (...) {}
0eae7b4d Poul Sander 2026-05-18 19:22 23
    return {};
0eae7b4d Poul Sander 2026-05-18 19:22 24
}
0eae7b4d Poul Sander 2026-05-18 19:22 25
0eae7b4d Poul Sander 2026-05-18 19:22 26
static void addField(QList<QPair<QString, QString>> &result, const char *label, const QString &value)
0eae7b4d Poul Sander 2026-05-18 19:22 27
{
0eae7b4d Poul Sander 2026-05-18 19:22 28
    if (!value.isEmpty())
0eae7b4d Poul Sander 2026-05-18 19:22 29
        result.append({QString::fromLatin1(label), value});
0eae7b4d Poul Sander 2026-05-18 19:22 30
}
0eae7b4d Poul Sander 2026-05-18 19:22 31
53fdab93 Poul Sander 2026-05-18 18:45 32
bool ExifData::isEmpty() const
53fdab93 Poul Sander 2026-05-18 18:45 33
{
53fdab93 Poul Sander 2026-05-18 18:45 34
    return dateTime.isEmpty() && make.isEmpty() && model.isEmpty()
53fdab93 Poul Sander 2026-05-18 18:45 35
        && exposureTime.isEmpty() && fNumber.isEmpty() && iso.isEmpty()
53fdab93 Poul Sander 2026-05-18 18:45 36
        && focalLength.isEmpty() && flash.isEmpty()
53fdab93 Poul Sander 2026-05-18 18:45 37
        && dimensions.isEmpty() && fileSize.isEmpty();
53fdab93 Poul Sander 2026-05-18 18:45 38
}
53fdab93 Poul Sander 2026-05-18 18:45 39
53fdab93 Poul Sander 2026-05-18 18:45 40
QList<QPair<QString, QString>> ExifData::toList() const
53fdab93 Poul Sander 2026-05-18 18:45 41
{
53fdab93 Poul Sander 2026-05-18 18:45 42
    QList<QPair<QString, QString>> result;
0eae7b4d Poul Sander 2026-05-18 19:22 43
    addField(result, "Date/Time",    dateTime);
0eae7b4d Poul Sander 2026-05-18 19:22 44
    addField(result, "Camera Make",  make);
0eae7b4d Poul Sander 2026-05-18 19:22 45
    addField(result, "Camera Model", model);
0eae7b4d Poul Sander 2026-05-18 19:22 46
    addField(result, "Exposure",     exposureTime);
0eae7b4d Poul Sander 2026-05-18 19:22 47
    addField(result, "Aperture",     fNumber);
0eae7b4d Poul Sander 2026-05-18 19:22 48
    addField(result, "ISO",          iso);
0eae7b4d Poul Sander 2026-05-18 19:22 49
    addField(result, "Focal Length", focalLength);
0eae7b4d Poul Sander 2026-05-18 19:22 50
    addField(result, "Flash",        flash);
0eae7b4d Poul Sander 2026-05-18 19:22 51
    addField(result, "Dimensions",   dimensions);
0eae7b4d Poul Sander 2026-05-18 19:22 52
    addField(result, "File Size",    fileSize);
53fdab93 Poul Sander 2026-05-18 18:45 53
    return result;
53fdab93 Poul Sander 2026-05-18 18:45 54
}
53fdab93 Poul Sander 2026-05-18 18:45 55
53fdab93 Poul Sander 2026-05-18 18:45 56
ExifData ExifReader::read(const QString &path)
53fdab93 Poul Sander 2026-05-18 18:45 57
{
53fdab93 Poul Sander 2026-05-18 18:45 58
    ExifData data;
53fdab93 Poul Sander 2026-05-18 18:45 59
53fdab93 Poul Sander 2026-05-18 18:45 60
    QFileInfo fi(path);
53fdab93 Poul Sander 2026-05-18 18:45 61
    if (!fi.exists() || fi.isDir())
53fdab93 Poul Sander 2026-05-18 18:45 62
        return data;
53fdab93 Poul Sander 2026-05-18 18:45 63
53fdab93 Poul Sander 2026-05-18 18:45 64
    // File size
53fdab93 Poul Sander 2026-05-18 18:45 65
    {
53fdab93 Poul Sander 2026-05-18 18:45 66
        qint64 size = fi.size();
53fdab93 Poul Sander 2026-05-18 18:45 67
        if (size < 1024)
53fdab93 Poul Sander 2026-05-18 18:45 68
            data.fileSize = QString::number(size) + QLatin1String(" B");
53fdab93 Poul Sander 2026-05-18 18:45 69
        else if (size < 1024 * 1024)
53fdab93 Poul Sander 2026-05-18 18:45 70
            data.fileSize = QString::number(size / 1024.0, 'f', 1) + QLatin1String(" KiB");
53fdab93 Poul Sander 2026-05-18 18:45 71
        else
53fdab93 Poul Sander 2026-05-18 18:45 72
            data.fileSize = QString::number(size / (1024.0 * 1024.0), 'f', 1) + QLatin1String(" MiB");
53fdab93 Poul Sander 2026-05-18 18:45 73
    }
53fdab93 Poul Sander 2026-05-18 18:45 74
53fdab93 Poul Sander 2026-05-18 18:45 75
    // Image dimensions via Qt (reads only the header, no full decode needed)
53fdab93 Poul Sander 2026-05-18 18:45 76
    {
53fdab93 Poul Sander 2026-05-18 18:45 77
        QImageReader reader(path);
53fdab93 Poul Sander 2026-05-18 18:45 78
        reader.setDecideFormatFromContent(true);
53fdab93 Poul Sander 2026-05-18 18:45 79
        QSize sz = reader.size();
53fdab93 Poul Sander 2026-05-18 18:45 80
        if (sz.isValid())
53fdab93 Poul Sander 2026-05-18 18:45 81
            data.dimensions = QString::fromLatin1("%1 * %2")
53fdab93 Poul Sander 2026-05-18 18:45 82
                                  .arg(sz.width()).arg(sz.height());
53fdab93 Poul Sander 2026-05-18 18:45 83
    }
53fdab93 Poul Sander 2026-05-18 18:45 84
53fdab93 Poul Sander 2026-05-18 18:45 85
    // EXIF tags via exiv2
53fdab93 Poul Sander 2026-05-18 18:45 86
    try {
0eae7b4d Poul Sander 2026-05-18 19:22 87
        std::unique_ptr<Exiv2::Image> image = Exiv2::ImageFactory::open(path.toStdString());
53fdab93 Poul Sander 2026-05-18 18:45 88
        image->readMetadata();
53fdab93 Poul Sander 2026-05-18 18:45 89
        const Exiv2::ExifData &exif = image->exifData();
53fdab93 Poul Sander 2026-05-18 18:45 90
0eae7b4d Poul Sander 2026-05-18 19:22 91
        data.dateTime = formatDate(findTag(exif, "Exif.Photo.DateTimeOriginal"));
53fdab93 Poul Sander 2026-05-18 18:45 92
        if (data.dateTime.isEmpty())
0eae7b4d Poul Sander 2026-05-18 19:22 93
            data.dateTime = formatDate(findTag(exif, "Exif.Image.DateTime"));
0eae7b4d Poul Sander 2026-05-18 19:22 94
        data.make         = findTag(exif, "Exif.Image.Make");
0eae7b4d Poul Sander 2026-05-18 19:22 95
        data.model        = findTag(exif, "Exif.Image.Model");
0eae7b4d Poul Sander 2026-05-18 19:22 96
        data.exposureTime = findTag(exif, "Exif.Photo.ExposureTime");
0eae7b4d Poul Sander 2026-05-18 19:22 97
        data.fNumber      = findTag(exif, "Exif.Photo.FNumber");
0eae7b4d Poul Sander 2026-05-18 19:22 98
        data.iso          = findTag(exif, "Exif.Photo.ISOSpeedRatings");
0eae7b4d Poul Sander 2026-05-18 19:22 99
        data.focalLength  = findTag(exif, "Exif.Photo.FocalLength");
0eae7b4d Poul Sander 2026-05-18 19:22 100
        data.flash        = findTag(exif, "Exif.Photo.Flash");
53fdab93 Poul Sander 2026-05-18 18:45 101
    } catch (const Exiv2::Error &) {
53fdab93 Poul Sander 2026-05-18 18:45 102
        // No EXIF or unsupported format — leave tags empty
53fdab93 Poul Sander 2026-05-18 18:45 103
    }
53fdab93 Poul Sander 2026-05-18 18:45 104
53fdab93 Poul Sander 2026-05-18 18:45 105
    return data;
53fdab93 Poul Sander 2026-05-18 18:45 106
}
1970-01-01 00:00 107