Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_iptc_io.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7#include "kis_iptc_io.h"
8
9#include <exiv2/iptc.hpp>
10
11#include <QIODevice>
12
13#include <kis_debug.h>
14#include <kis_exiv2_common.h>
15#include <kis_meta_data_entry.h>
18#include <kis_meta_data_store.h>
19#include <kis_meta_data_value.h>
20
21const char photoshopMarker[] = "Photoshop 3.0\0";
22const char photoshopBimId_[] = "8BIM";
23const uint16_t photoshopIptc = 0x0404;
24const QByteArray photoshopIptc_((char *)&photoshopIptc, 2);
25
26struct IPTCToKMD {
27 QString exivTag;
28 QString namespaceUri;
29 QString name;
30};
31
32static const IPTCToKMD mappings[] = {
33 {"Iptc.Application2.City", KisMetaData::Schema::PhotoshopSchemaUri, "City"},
34 {"Iptc.Application2.Copyright", KisMetaData::Schema::DublinCoreSchemaUri, "rights"},
35 {"Iptc.Application2.CountryName", KisMetaData::Schema::PhotoshopSchemaUri, "Country"},
36 {"Iptc.Application2.CountryCode", KisMetaData::Schema::IPTCSchemaUri, "CountryCode"},
37 {"Iptc.Application2.Byline", KisMetaData::Schema::DublinCoreSchemaUri, "creator"},
38 {"Iptc.Application2.BylineTitle", KisMetaData::Schema::PhotoshopSchemaUri, "AuthorsPosition"},
39 {"Iptc.Application2.DateCreated", KisMetaData::Schema::PhotoshopSchemaUri, "DateCreated"},
40 {"Iptc.Application2.Caption", KisMetaData::Schema::DublinCoreSchemaUri, "description"},
41 {"Iptc.Application2.Writer", KisMetaData::Schema::PhotoshopSchemaUri, "CaptionWriter"},
42 {"Iptc.Application2.Headline", KisMetaData::Schema::PhotoshopSchemaUri, "Headline"},
43 {"Iptc.Application2.SpecialInstructions", KisMetaData::Schema::PhotoshopSchemaUri, "Instructions"},
44 {"Iptc.Application2.ObjectAttribute", KisMetaData::Schema::IPTCSchemaUri, "IntellectualGenre"},
45 {"Iptc.Application2.TransmissionReference", KisMetaData::Schema::PhotoshopSchemaUri, "JobID"},
46 {"Iptc.Application2.Keywords", KisMetaData::Schema::DublinCoreSchemaUri, "subject"},
47 {"Iptc.Application2.SubLocation", KisMetaData::Schema::IPTCSchemaUri, "Location"},
48 {"Iptc.Application2.Credit", KisMetaData::Schema::PhotoshopSchemaUri, "Credit"},
49 {"Iptc.Application2.ProvinceState", KisMetaData::Schema::PhotoshopSchemaUri, "State"},
50 {"Iptc.Application2.Source", KisMetaData::Schema::PhotoshopSchemaUri, "Source"},
51 {"Iptc.Application2.Subject", KisMetaData::Schema::IPTCSchemaUri, "SubjectCode"},
52 {"Iptc.Application2.ObjectName", KisMetaData::Schema::DublinCoreSchemaUri, "title"},
53 {"Iptc.Application2.Urgency", KisMetaData::Schema::PhotoshopSchemaUri, "Urgency"},
54 {"Iptc.Application2.Category", KisMetaData::Schema::PhotoshopSchemaUri, "Category"},
55 {"Iptc.Application2.SuppCategory", KisMetaData::Schema::PhotoshopSchemaUri, "SupplementalCategory"},
56 {"", "", ""} // indicates the end of the array
57};
58
60 QHash<QString, IPTCToKMD> iptcToKMD;
61 QHash<QString, IPTCToKMD> kmdToIPTC;
62};
63
64// ---- Implementation of KisIptcIO ----//
66 : KisMetaData::IOBackend()
67 , d(new Private)
68{
69}
70
72{
73 delete d;
74}
75
77{
78 // For some reason, initializing the tables in the constructor makes the it crash
79 if (d->iptcToKMD.size() == 0) {
80 for (int i = 0; !mappings[i].exivTag.isEmpty(); i++) {
81 dbgKrita << "mapping[i] = " << mappings[i].exivTag << " " << mappings[i].namespaceUri << " "
82 << mappings[i].name;
85 ->schemaFromUri(mappings[i].namespaceUri)
87 }
88 }
89}
90
91bool KisIptcIO::saveTo(const KisMetaData::Store *store, QIODevice *ioDevice, HeaderType headerType) const
92{
93 QStringList blockedEntries = QStringList() << "photoshop:DateCreated";
94
96 ioDevice->open(QIODevice::WriteOnly);
97 Exiv2::IptcData iptcData;
98 for (const KisMetaData::Entry &entry : *store) {
99 if (d->kmdToIPTC.contains(entry.qualifiedName())) {
100 if (blockedEntries.contains(entry.qualifiedName())) {
101 warnKrita << "skipping" << entry.qualifiedName() << entry.value();
102 continue;
103 }
104 try {
105 QString iptcKeyStr = d->kmdToIPTC[entry.qualifiedName()].exivTag;
106 Exiv2::IptcKey iptcKey(qPrintable(iptcKeyStr));
107 Exiv2::Value *v =
108 kmdValueToExivValue(entry.value(),
109 Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record()));
110
111 if (v && v->typeId() != Exiv2::invalidTypeId) {
112 iptcData.add(iptcKey, v);
113 }
114#if EXIV2_TEST_VERSION(0,28,0)
115 } catch (Exiv2::Error &e) {
116#else
117 } catch (Exiv2::AnyError &e) {
118#endif
119 dbgMetaData << "exiv error " << e.what();
120 }
121 }
122 }
123#if !EXIV2_TEST_VERSION(0, 18, 0)
124 Exiv2::DataBuf rawData = iptcData.copy();
125#else
126 Exiv2::DataBuf rawData = Exiv2::IptcParser::encode(iptcData);
127#endif
128
129 if (headerType == KisMetaData::IOBackend::JpegHeader) {
130 QByteArray header;
131 header.append(photoshopMarker);
132 header.append(QByteArray(1, 0)); // Null terminated string
133 header.append(photoshopBimId_);
134 header.append(photoshopIptc_);
135 header.append(QByteArray(2, 0));
136#if EXIV2_TEST_VERSION(0, 28, 0)
137 qint32 size = rawData.size();
138#else
139 qint32 size = rawData.size_;
140#endif
141 QByteArray sizeArray(4, 0);
142 sizeArray[0] = (char)((size & 0xff000000) >> 24);
143 sizeArray[1] = (char)((size & 0x00ff0000) >> 16);
144 sizeArray[2] = (char)((size & 0x0000ff00) >> 8);
145 sizeArray[3] = (char)(size & 0x000000ff);
146 header.append(sizeArray);
147 ioDevice->write(header);
148 }
149
150#if EXIV2_TEST_VERSION(0, 28, 0)
151 ioDevice->write((const char *)rawData.data(), rawData.size());
152#else
153 ioDevice->write((const char *)rawData.pData_, rawData.size_);
154#endif
155 ioDevice->close();
156 return true;
157}
158
160{
161 Q_UNUSED(store);
162 return false;
163}
164
165bool KisIptcIO::loadFrom(KisMetaData::Store *store, QIODevice *ioDevice) const
166{
168 dbgMetaData << "Loading IPTC Tags";
169 ioDevice->open(QIODevice::ReadOnly);
170 QByteArray arr = ioDevice->readAll();
171 Exiv2::IptcData iptcData;
172#if !EXIV2_TEST_VERSION(0, 18, 0)
173 iptcData.load((const Exiv2::byte *)arr.data(), arr.size());
174#else
175 Exiv2::IptcParser::decode(iptcData, (const Exiv2::byte *)arr.data(), arr.size());
176#endif
177 dbgMetaData << "There are" << iptcData.count() << " entries in the IPTC section";
178 for (Exiv2::IptcMetadata::const_iterator it = iptcData.begin(); it != iptcData.end(); ++it) {
179 dbgMetaData << "Reading info for key" << it->key().c_str();
180 if (d->iptcToKMD.contains(it->key().c_str())) {
181 const IPTCToKMD &iptcToKMd = d->iptcToKMD[it->key().c_str()];
182 const KisMetaData::Schema *schema =
185 if (iptcToKMd.exivTag == "Iptc.Application2.Keywords") {
186 Q_ASSERT(it->getValue()->typeId() == Exiv2::string);
187 QString data = it->getValue()->toString().c_str();
188
189 QStringList list = data.split(',');
191 Q_FOREACH (const QString &entry, list) {
192 values.push_back(KisMetaData::Value(entry));
193 }
195 } else {
196 value = exivValueToKMDValue(it->getValue(), false);
197 }
198 store->addEntry(KisMetaData::Entry(schema, iptcToKMd.name, value));
199 }
200 }
201 return false;
202}
float value(const T *src, size_t ch)
qreal v
QList< QString > QStringList
bool loadFrom(KisMetaData::Store *store, QIODevice *ioDevice) const override
void initMappingsTable() const
bool saveTo(const KisMetaData::Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const override
bool canSaveAllEntries(KisMetaData::Store *store) const override
Private *const d
Definition kis_iptc_io.h:49
QString name() const override
Definition kis_iptc_io.h:24
~KisIptcIO() override
@ JpegHeader
Append Jpeg-style header.
static KisMetaData::SchemaRegistry * instance()
const Schema * schemaFromUri(const QString &uri) const
QString generateQualifiedName(const QString &) const
static const QString PhotoshopSchemaUri
static const QString IPTCSchemaUri
static const QString DublinCoreSchemaUri
bool addEntry(const Entry &entry)
#define dbgKrita
Definition kis_debug.h:45
#define warnKrita
Definition kis_debug.h:87
#define dbgMetaData
Definition kis_debug.h:61
Exiv2::Value * kmdValueToExivValue(const KisMetaData::Value &value, Exiv2::TypeId type)
Convert a KisMetaData to an Exiv value.
KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr &value, bool forceSeq, KisMetaData::Value::ValueType arrayType=KisMetaData::Value::UnorderedArray)
static const IPTCToKMD mappings[]
const uint16_t photoshopIptc
const char photoshopMarker[]
const char photoshopBimId_[]
const QByteArray photoshopIptc_((char *)&photoshopIptc, 2)
const char photoshopMarker[]
const QByteArray photoshopIptc_((char *)&photoshopIptc, 2)
QString name
QString exivTag
QString namespaceUri
QHash< QString, IPTCToKMD > iptcToKMD
QHash< QString, IPTCToKMD > kmdToIPTC