1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2025 Mirco Miranda <mircomir@outlook.com>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#ifndef MICROEXIF_P_H
9#define MICROEXIF_P_H
10
11#include <QByteArray>
12#include <QColorSpace>
13#include <QDataStream>
14#include <QDateTime>
15#include <QImage>
16#include <QImageIOHandler>
17#include <QMap>
18#include <QUuid>
19#include <QVariant>
20
21#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
22#define EXIF_DEFAULT_BYTEORDER QDataStream::LittleEndian
23#else
24#define EXIF_DEFAULT_BYTEORDER QDataStream::BigEndian
25#endif
26
27/*!
28 * \brief The MicroExif class
29 * Class to extract / write minimal EXIF data (e.g. resolution, rotation,
30 * some strings).
31 *
32 * This class is a partial (or rather minimal) implementation and is only used
33 * to avoid including external libraries when only a few tags are needed.
34 */
35class MicroExif
36{
37public:
38 using Tags = QMap<quint16, QVariant>;
39
40 /*!
41 * \brief The Version enum
42 * Exif specs version used when writing.
43 */
44 enum Version {
45 V2, // V2.xx
46 V3 // V3.xx, use of UTF-8 data type (default)
47 };
48
49 /*!
50 * \brief MicroExif
51 * Constructs an empty class.
52 * \sa isEmpty
53 */
54 MicroExif();
55
56 MicroExif(const MicroExif &other) = default;
57 MicroExif &operator=(const MicroExif &other) = default;
58
59 /*!
60 * \brief clear
61 * Removes all items.
62 */
63 void clear();
64
65 /*!
66 * \brief isEmpty
67 * \return True if it contains no items, otherwise false.
68 */
69 bool isEmpty() const;
70
71 /*!
72 * \brief horizontalResolution
73 * \return The horizontal resolution in DPI.
74 */
75 double horizontalResolution() const;
76 void setHorizontalResolution(double hres);
77
78 /*!
79 * \brief verticalResolution
80 * \return The vertical resolution in DPI.
81 */
82 double verticalResolution() const;
83 void setVerticalResolution(double vres);
84
85 /*!
86 * \brief colosSpace
87 * \return sRGB color space or an invalid one.
88 */
89 QColorSpace colosSpace() const;
90 void setColorSpace(const QColorSpace& cs);
91 void setColorSpace(const QColorSpace::NamedColorSpace& csName);
92
93 /*!
94 * \brief width
95 * \return The image width.
96 */
97 qint32 width() const;
98 void setWidth(qint32 w);
99
100 /*!
101 * \brief height
102 * \return The image height.
103 */
104 qint32 height() const;
105 void setHeight(qint32 h);
106
107 /*!
108 * \brief orientation
109 * The orientation of the image with respect to the rows and columns.
110 *
111 * Valid orientation values:
112 * - 1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
113 * - 2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
114 * - 3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
115 * - 4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
116 * - 5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
117 * - 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
118 * - 7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
119 * - 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
120 * \return The orientation value or 0 if none.
121 * \sa transformation
122 */
123 quint16 orientation() const;
124 void setOrientation(quint16 orient);
125
126 /*!
127 * \brief transformation
128 * \return The orientation converted in the equvalent Qt transformation.
129 * \sa orientation
130 */
131 QImageIOHandler::Transformation transformation() const;
132 void setTransformation(const QImageIOHandler::Transformation& t);
133
134 /*!
135 * \brief software
136 * \return Name and version number of the software package(s) used to create the image.
137 */
138 QString software() const;
139 void setSoftware(const QString& s);
140
141 /*!
142 * \brief description
143 * \return A string that describes the subject of the image.
144 */
145 QString description() const;
146 void setDescription(const QString& s);
147
148 /*!
149 * \brief artist
150 * \return Person who created the image.
151 */
152 QString artist() const;
153 void setArtist(const QString& s);
154
155 /*!
156 * \brief copyright
157 * \return Copyright notice of the person or organization that claims the copyright to the image.
158 */
159 QString copyright() const;
160 void setCopyright(const QString& s);
161
162 /*!
163 * \brief make
164 * \return The manufacturer of the recording equipment.
165 */
166 QString make() const;
167 void setMake(const QString& s);
168
169 /*!
170 * \brief model
171 * \return The model name or model number of the equipment.
172 */
173 QString model() const;
174 void setModel(const QString& s);
175
176 /*!
177 * \brief serialNumber
178 * \return The serial number of the recording equipment.
179 */
180 QString serialNumber() const;
181 void setSerialNumber(const QString &s);
182
183 /*!
184 * \brief lensMake
185 * \return The manufacturer of the interchangeable lens that was used.
186 */
187 QString lensMake() const;
188 void setLensMake(const QString &s);
189
190 /*!
191 * \brief lensModel
192 * \return The model name or model number of the lens that was used.
193 */
194 QString lensModel() const;
195 void setLensModel(const QString &s);
196
197 /*!
198 * \brief lensSerialNumber
199 * \return The serial number of the interchangeable lens that was used.
200 */
201 QString lensSerialNumber() const;
202 void setLensSerialNumber(const QString &s);
203
204 /*!
205 * \brief dateTime
206 * \return Creation date and time.
207 */
208 QDateTime dateTime() const;
209 void setDateTime(const QDateTime& dt);
210
211 /*!
212 * \brief dateTimeOriginal
213 * \return The date and time when the original image data was generated.
214 */
215 QDateTime dateTimeOriginal() const;
216 void setDateTimeOriginal(const QDateTime& dt);
217
218 /*!
219 * \brief dateTimeDigitized
220 * \return The date and time when the image was stored as digital data.
221 */
222 QDateTime dateTimeDigitized() const;
223 void setDateTimeDigitized(const QDateTime& dt);
224
225 /*!
226 * \brief title
227 * \return The title of the image.
228 */
229 QString title() const;
230 void setImageTitle(const QString &s);
231
232 /*!
233 * \brief uniqueId
234 * \return An identifier assigned uniquely to each image or null one if none.
235 */
236 QUuid uniqueId() const;
237 void setUniqueId(const QUuid &uuid);
238
239 /*!
240 * \brief latitude
241 * \return Floating-point number indicating the latitude in degrees north of the equator (e.g. 27.717) or NaN if not set.
242 */
243 double latitude() const;
244 void setLatitude(double degree);
245
246 /*!
247 * \brief longitude
248 * \return Floating-point number indicating the longitude in degrees east of Greenwich (e.g. 85.317) or NaN if not set.
249 */
250 double longitude() const;
251 void setLongitude(double degree);
252
253 /*!
254 * \brief altitude
255 * \return Floating-point number indicating the GPS altitude in meters above sea level or ellipsoidal surface (e.g. 35.4) or NaN if not set.
256 * \note It makes no distinction between an 'ellipsoidal surface' and 'sea level'.
257 */
258 double altitude() const;
259 void setAltitude(double meters);
260
261 /*!
262 * \brief imageDirection
263 * \param isMagnetic Set to true if the direction is relative to magnetic north, false if it is relative to true north. Leave nullptr if is not of interest.
264 * \return Floating-point number indicating the direction of the image when it was captured. The range of values is from 0.00 to 359.99 or NaN if not set.
265 */
266 double imageDirection(bool *isMagnetic = nullptr) const;
267 void setImageDirection(double degree, bool isMagnetic = false);
268
269 /*!
270 * \brief toByteArray
271 * Converts the class to RAW data. The raw data contains:
272 * - TIFF header
273 * - MAIN IFD
274 * - EXIF IFD
275 * - GPS IFD
276 * \param byteOrder Sets the serialization byte order for EXIF data.
277 * \param version The EXIF specs version to use.
278 * \return A byte array containing the serialized data.
279 * \sa write
280 */
281 QByteArray toByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
282
283 /*!
284 * \brief exifIfdByteArray
285 * Convert the EXIF IFD only to RAW data. Useful when you want to add EXIF data to an existing TIFF container.
286 * \param byteOrder Sets the serialization byte order for the data.
287 * \param version The EXIF specs version to use.
288 * \return A byte array containing the serialized data.
289 */
290 QByteArray exifIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
291 /*!
292 * \brief setExifIfdByteArray
293 * \param ba The RAW data of EXIF IFD.
294 * \param byteOrder Sets the serialization byte order of the data.
295 * \return True on success, otherwise false.
296 */
297 bool setExifIfdByteArray(const QByteArray& ba, const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER);
298
299 /*!
300 * \brief gpsIfdByteArray
301 * Convert the GPS IFD only to RAW data. Useful when you want to add GPS data to an existing TIFF container.
302 * \param byteOrder Sets the serialization byte order for the data.
303 * \param version The EXIF specs version to use.
304 * \return A byte array containing the serialized data.
305 */
306 QByteArray gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
307 /*!
308 * \brief setGpsIfdByteArray
309 * \param ba The RAW data of GPS IFD.
310 * \param byteOrder Sets the serialization byte order of the data.
311 * \return True on success, otherwise false.
312 */
313 bool setGpsIfdByteArray(const QByteArray& ba, const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER);
314
315 /*!
316 * \brief write
317 * Serialize the class on a device. The serialized data contains:
318 * - TIFF header
319 * - MAIN IFD
320 * - EXIF IFD
321 * - GPS IFD
322 * \param device A random access device.
323 * \param byteOrder Sets the serialization byte order for EXIF data.
324 * \param version The EXIF specs version to use.
325 * \return True on success, otherwise false.
326 * \sa toByteArray
327 */
328 bool write(QIODevice *device, const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
329
330 /*!
331 * \brief updateImageMetadata
332 * Helper to set EXIF metadata to the image.
333 * \param targetImage The image to set metadata on.
334 * \param replaceExisting Replaces any existing metadata.
335 */
336 void updateImageMetadata(QImage &targetImage, bool replaceExisting = false) const;
337
338 /*!
339 * \brief updateImageResolution
340 * Helper to set the EXIF resolution to the image. Resolution is set only if valid.
341 * \param targetImage The image to set resolution on.
342 * \return True if either the x-resolution or the y-resolution has been changed, otherwise false.
343 */
344 bool updateImageResolution(QImage &targetImage);
345
346 /*!
347 * \brief fromByteArray
348 * Creates the class from RAW EXIF data.
349 * \param ba Raw data containing EXIF ​​data.
350 * \param searchHeader If true, the EXIF ​​header is searched within the data. If false, the data must begin with the EXIF ​​header.
351 * \return The created class (empty on error).
352 * \sa isEmpty
353 */
354 static MicroExif fromByteArray(const QByteArray &ba, bool searchHeader = false);
355
356 /*!
357 * \brief fromRawData
358 * Creates the class from RAW EXIF data.
359 * \param data Raw data containing EXIF ​​data.
360 * \param size The size of \a data.
361 * \param searchHeader If true, the EXIF ​​header is searched within the data. If false, the data must begin with the EXIF ​​header.
362 * \return The created class (empty on error).
363 * \sa isEmpty, fromByteArray
364 */
365 static MicroExif fromRawData(const char *data, size_t size, bool searchHeader = false);
366
367 /*!
368 * \brief fromDevice
369 * Creates the class from a device.
370 * \param device A random access device.
371 * \return The created class (empty on error).
372 * \sa isEmpty
373 */
374 static MicroExif fromDevice(QIODevice *device);
375
376 /*!
377 * \brief fromImage
378 * Creates the class and fill it with image info (e.g. resolution).
379 */
380 static MicroExif fromImage(const QImage &image);
381
382private:
383 void setTiffString(quint16 tagId, const QString &s);
384 QString tiffString(quint16 tagId) const;
385 void setExifString(quint16 tagId, const QString& s);
386 QString exifString(quint16 tagId) const;
387 void setGpsString(quint16 tagId, const QString& s);
388 QString gpsString(quint16 tagId) const;
389 bool writeHeader(QDataStream &ds) const;
390 bool writeIfds(QDataStream &ds, const Version &version) const;
391 void updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags, const Version &version) const;
392
393 static void setString(Tags &tags, quint16 tagId, const QString &s);
394 static QString string(const Tags &tags, quint16 tagId);
395
396private:
397 Tags m_tiffTags;
398 Tags m_exifTags;
399 Tags m_gpsTags;
400};
401
402#endif // MICROEXIF_P_H
403

source code of kimageformats/src/imageformats/microexif_p.h