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#include <QRegExp>
13
14#include <kis_debug.h>
15#include <kis_exiv2_common.h>
16#include <kis_meta_data_entry.h>
19#include <kis_meta_data_store.h>
20#include <kis_meta_data_value.h>
21
22const char photoshopMarker[] = "Photoshop 3.0\0";
23const char photoshopBimId_[] = "8BIM";
24const uint16_t photoshopIptc = 0x0404;
25const QByteArray photoshopIptc_((char *)&photoshopIptc, 2);
26
27struct IPTCToKMD {
28 QString exivTag;
29 QString namespaceUri;
30 QString name;
31};
32
33static const IPTCToKMD mappings[] = {
34 {"Iptc.Application2.City", KisMetaData::Schema::PhotoshopSchemaUri, "City"},
35 {"Iptc.Application2.Copyright", KisMetaData::Schema::DublinCoreSchemaUri, "rights"},
36 {"Iptc.Application2.CountryName", KisMetaData::Schema::PhotoshopSchemaUri, "Country"},
37 {"Iptc.Application2.CountryCode", KisMetaData::Schema::IPTCSchemaUri, "CountryCode"},
38 {"Iptc.Application2.Byline", KisMetaData::Schema::DublinCoreSchemaUri, "creator"},
39 {"Iptc.Application2.BylineTitle", KisMetaData::Schema::PhotoshopSchemaUri, "AuthorsPosition"},
40 {"Iptc.Application2.DateCreated", KisMetaData::Schema::PhotoshopSchemaUri, "DateCreated"},
41 {"Iptc.Application2.Caption", KisMetaData::Schema::DublinCoreSchemaUri, "description"},
42 {"Iptc.Application2.Writer", KisMetaData::Schema::PhotoshopSchemaUri, "CaptionWriter"},
43 {"Iptc.Application2.Headline", KisMetaData::Schema::PhotoshopSchemaUri, "Headline"},
44 {"Iptc.Application2.SpecialInstructions", KisMetaData::Schema::PhotoshopSchemaUri, "Instructions"},
45 {"Iptc.Application2.ObjectAttribute", KisMetaData::Schema::IPTCSchemaUri, "IntellectualGenre"},
46 {"Iptc.Application2.TransmissionReference", KisMetaData::Schema::PhotoshopSchemaUri, "JobID"},
47 {"Iptc.Application2.Keywords", KisMetaData::Schema::DublinCoreSchemaUri, "subject"},
48 {"Iptc.Application2.SubLocation", KisMetaData::Schema::IPTCSchemaUri, "Location"},
49 {"Iptc.Application2.Credit", KisMetaData::Schema::PhotoshopSchemaUri, "Credit"},
50 {"Iptc.Application2.ProvinceState", KisMetaData::Schema::PhotoshopSchemaUri, "State"},
51 {"Iptc.Application2.Source", KisMetaData::Schema::PhotoshopSchemaUri, "Source"},
52 {"Iptc.Application2.Subject", KisMetaData::Schema::IPTCSchemaUri, "SubjectCode"},
53 {"Iptc.Application2.ObjectName", KisMetaData::Schema::DublinCoreSchemaUri, "title"},
54 {"Iptc.Application2.Urgency", KisMetaData::Schema::PhotoshopSchemaUri, "Urgency"},
55 {"Iptc.Application2.Category", KisMetaData::Schema::PhotoshopSchemaUri, "Category"},
56 {"Iptc.Application2.SuppCategory", KisMetaData::Schema::PhotoshopSchemaUri, "SupplementalCategory"},
57 {"", "", ""} // indicates the end of the array
58};
59
61 QHash<QString, IPTCToKMD> iptcToKMD;
62 QHash<QString, IPTCToKMD> kmdToIPTC;
63};
64
65// ---- Implementation of KisIptcIO ----//
67 : KisMetaData::IOBackend()
68 , d(new Private)
69{
70}
71
73{
74 delete d;
75}
76
78{
79 // For some reason, initializing the tables in the constructor makes the it crash
80 if (d->iptcToKMD.size() == 0) {
81 for (int i = 0; !mappings[i].exivTag.isEmpty(); i++) {
82 dbgKrita << "mapping[i] = " << mappings[i].exivTag << " " << mappings[i].namespaceUri << " "
83 << mappings[i].name;
86 ->schemaFromUri(mappings[i].namespaceUri)
88 }
89 }
90}
91
92bool KisIptcIO::saveTo(const KisMetaData::Store *store, QIODevice *ioDevice, HeaderType headerType) const
93{
94 QStringList blockedEntries = QStringList() << "photoshop:DateCreated";
95
97 ioDevice->open(QIODevice::WriteOnly);
98 Exiv2::IptcData iptcData;
99 for (const KisMetaData::Entry &entry : *store) {
100 if (d->kmdToIPTC.contains(entry.qualifiedName())) {
101 if (blockedEntries.contains(entry.qualifiedName())) {
102 warnKrita << "skipping" << entry.qualifiedName() << entry.value();
103 continue;
104 }
105 try {
106 QString iptcKeyStr = d->kmdToIPTC[entry.qualifiedName()].exivTag;
107 Exiv2::IptcKey iptcKey(qPrintable(iptcKeyStr));
108 Exiv2::Value *v =
109 kmdValueToExivValue(entry.value(),
110 Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record()));
111
112 if (v && v->typeId() != Exiv2::invalidTypeId) {
113 iptcData.add(iptcKey, v);
114 }
115#if EXIV2_TEST_VERSION(0,28,0)
116 } catch (Exiv2::Error &e) {
117#else
118 } catch (Exiv2::AnyError &e) {
119#endif
120 dbgMetaData << "exiv error " << e.what();
121 }
122 }
123 }
124#if !EXIV2_TEST_VERSION(0, 18, 0)
125 Exiv2::DataBuf rawData = iptcData.copy();
126#else
127 Exiv2::DataBuf rawData = Exiv2::IptcParser::encode(iptcData);
128#endif
129
130 if (headerType == KisMetaData::IOBackend::JpegHeader) {
131 QByteArray header;
132 header.append(photoshopMarker);
133 header.append(QByteArray(1, 0)); // Null terminated string
134 header.append(photoshopBimId_);
135 header.append(photoshopIptc_);
136 header.append(QByteArray(2, 0));
137#if EXIV2_TEST_VERSION(0, 28, 0)
138 qint32 size = rawData.size();
139#else
140 qint32 size = rawData.size_;
141#endif
142 QByteArray sizeArray(4, 0);
143 sizeArray[0] = (char)((size & 0xff000000) >> 24);
144 sizeArray[1] = (char)((size & 0x00ff0000) >> 16);
145 sizeArray[2] = (char)((size & 0x0000ff00) >> 8);
146 sizeArray[3] = (char)(size & 0x000000ff);
147 header.append(sizeArray);
148 ioDevice->write(header);
149 }
150
151#if EXIV2_TEST_VERSION(0, 28, 0)
152 ioDevice->write((const char *)rawData.data(), rawData.size());
153#else
154 ioDevice->write((const char *)rawData.pData_, rawData.size_);
155#endif
156 ioDevice->close();
157 return true;
158}
159
161{
162 Q_UNUSED(store);
163 return false;
164}
165
166bool KisIptcIO::loadFrom(KisMetaData::Store *store, QIODevice *ioDevice) const
167{
169 dbgMetaData << "Loading IPTC Tags";
170 ioDevice->open(QIODevice::ReadOnly);
171 QByteArray arr = ioDevice->readAll();
172 Exiv2::IptcData iptcData;
173#if !EXIV2_TEST_VERSION(0, 18, 0)
174 iptcData.load((const Exiv2::byte *)arr.data(), arr.size());
175#else
176 Exiv2::IptcParser::decode(iptcData, (const Exiv2::byte *)arr.data(), arr.size());
177#endif
178 dbgMetaData << "There are" << iptcData.count() << " entries in the IPTC section";
179 for (Exiv2::IptcMetadata::const_iterator it = iptcData.begin(); it != iptcData.end(); ++it) {
180 dbgMetaData << "Reading info for key" << it->key().c_str();
181 if (d->iptcToKMD.contains(it->key().c_str())) {
182 const IPTCToKMD &iptcToKMd = d->iptcToKMD[it->key().c_str()];
183 const KisMetaData::Schema *schema =
186 if (iptcToKMd.exivTag == "Iptc.Application2.Keywords") {
187 Q_ASSERT(it->getValue()->typeId() == Exiv2::string);
188 QString data = it->getValue()->toString().c_str();
189
190 QStringList list = data.split(',');
192 Q_FOREACH (const QString &entry, list) {
193 values.push_back(KisMetaData::Value(entry));
194 }
196 } else {
197 value = exivValueToKMDValue(it->getValue(), false);
198 }
199 store->addEntry(KisMetaData::Entry(schema, iptcToKMd.name, value));
200 }
201 }
202 return false;
203}
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