Coverage Report

Created: 2026-09-07 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/pgfimage.cpp
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
// included header files
4
#include "pgfimage.hpp"
5
6
#include "basicio.hpp"
7
#include "config.h"
8
#include "enforce.hpp"
9
#include "error.hpp"
10
#include "futils.hpp"
11
#include "image.hpp"
12
#include "types.hpp"
13
14
#include <array>
15
#include <cstdint>
16
#include <cstring>
17
#include <limits>
18
#include <utility>
19
20
#ifdef EXIV2_DEBUG_MESSAGES
21
#include <iostream>
22
#endif
23
24
// Signature from front of PGF file
25
const std::array<unsigned char, 3> pgfSignature{0x50, 0x47, 0x46};
26
27
const unsigned char pgfBlank[] = {
28
    0x50, 0x47, 0x46, 0x36, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
29
    0x18, 0x03, 0x03, 0x00, 0x00, 0x00, 0x14, 0x00, 0x67, 0x08, 0x20, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x37, 0x00,
30
    0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
31
    0x01, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x37, 0x00,
32
    0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
33
    0x01, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00};
34
35
// *****************************************************************************
36
// class member definitions
37
38
namespace Exiv2 {
39
6.22k
static uint32_t byteSwap_(Exiv2::DataBuf& buf, size_t offset, bool bSwap) {
40
6.22k
  uint32_t v = 0;
41
6.22k
  auto p = reinterpret_cast<byte*>(&v);
42
6.22k
  int i;
43
31.1k
  for (i = 0; i < 4; i++)
44
24.9k
    p[i] = buf.read_uint8(offset + i);
45
6.22k
  uint32_t result = Image::byteSwap(v, bSwap);
46
6.22k
  p = reinterpret_cast<byte*>(&result);
47
31.1k
  for (i = 0; i < 4; i++)
48
24.9k
    buf.write_uint8(offset + i, p[i]);
49
6.22k
  return result;
50
6.22k
}
51
52
PgfImage::PgfImage(BasicIo::UniquePtr io, const ImageCtorParams& params) :
53
1.53k
    Image(ImageType::pgf, mdExif | mdIptc | mdXmp | mdComment, std::move(io), params), bSwap_(isBigEndianPlatform()) {
54
1.53k
  if (params.create() && io_->open() == 0) {
55
#ifdef EXIV2_DEBUG_MESSAGES
56
    std::cerr << "Exiv2::PgfImage:: Creating PGF image to memory\n";
57
#endif
58
0
    IoCloser closer(*io_);
59
0
    if (io_->write(pgfBlank, sizeof(pgfBlank)) != sizeof(pgfBlank)) {
60
#ifdef EXIV2_DEBUG_MESSAGES
61
      std::cerr << "Exiv2::PgfImage:: Failed to create PGF image on memory\n";
62
#endif
63
0
    }
64
0
  }
65
1.53k
}  // PgfImage::PgfImage
66
67
1.53k
void PgfImage::readMetadata() {
68
#ifdef EXIV2_DEBUG_MESSAGES
69
  std::cerr << "Exiv2::PgfImage::readMetadata: Reading PGF file " << io_->path() << "\n";
70
#endif
71
1.53k
  if (io_->open() != 0) {
72
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
73
0
  }
74
1.53k
  IoCloser closer(*io_);
75
  // Ensure that this is the correct image type
76
1.53k
  if (!isPgfType(*io_, true)) {
77
0
    if (io_->error() || io_->eof())
78
0
      throw Error(ErrorCode::kerFailedToReadImageData);
79
0
    throw Error(ErrorCode::kerNotAnImage, "PGF");
80
0
  }
81
1.53k
  clearMetadata();
82
83
1.53k
  readPgfMagicNumber(*io_);
84
85
1.53k
  size_t headerSize = readPgfHeaderSize(*io_);
86
1.53k
  readPgfHeaderStructure(*io_, pixelWidth_, pixelHeight_);
87
88
  // And now, the most interesting, the user data byte array where metadata are stored as small image.
89
90
1.53k
  Internal::enforce(headerSize <= std::numeric_limits<size_t>::max() - 8, ErrorCode::kerCorruptedMetadata);
91
1.53k
  size_t size = headerSize + 8 - io_->tell();
92
93
#ifdef EXIV2_DEBUG_MESSAGES
94
  std::cout << "Exiv2::PgfImage::readMetadata: Found Image data (" << size << " bytes)\n";
95
#endif
96
97
1.53k
  const size_t offset = io_->tell();
98
1.53k
  assert(offset <= io_->size());
99
1.53k
  if (size > io_->size() - offset)
100
269
    throw Error(ErrorCode::kerInputDataReadFailed);
101
1.26k
  if (size == 0)
102
41
    return;
103
104
1.22k
  const Exiv2::byte* base = io_->mmap();
105
1.22k
  if (io_->error())
106
0
    throw Error(ErrorCode::kerFailedToReadImageData);
107
108
1.22k
  auto image = Exiv2::ImageFactory::open(base + offset, size);
109
1.22k
  image->readMetadata();
110
1.22k
  exifData() = image->exifData();
111
1.22k
  iptcData() = image->iptcData();
112
1.22k
  xmpData() = image->xmpData();
113
1.22k
}
114
115
427
void PgfImage::writeMetadata() {
116
427
  if (io_->open() != 0) {
117
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
118
0
  }
119
427
  IoCloser closer(*io_);
120
427
  MemIo tempIo;
121
122
427
  doWriteMetadata(tempIo);  // may throw
123
427
  io_->close();
124
427
  io_->transfer(tempIo);  // may throw
125
126
427
}  // PgfImage::writeMetadata
127
128
427
void PgfImage::doWriteMetadata(BasicIo& outIo) {
129
427
  if (!io_->isopen())
130
0
    throw Error(ErrorCode::kerInputDataReadFailed);
131
427
  if (!outIo.isopen())
132
0
    throw Error(ErrorCode::kerImageWriteFailed);
133
134
#ifdef EXIV2_DEBUG_MESSAGES
135
  std::cout << "Exiv2::PgfImage::doWriteMetadata: Writing PGF file " << io_->path() << "\n";
136
  std::cout << "Exiv2::PgfImage::doWriteMetadata: tmp file created " << outIo.path() << "\n";
137
#endif
138
139
  // Ensure that this is the correct image type
140
427
  if (!isPgfType(*io_, true)) {
141
0
    if (io_->error() || io_->eof())
142
0
      throw Error(ErrorCode::kerInputDataReadFailed);
143
0
    throw Error(ErrorCode::kerNoImageInInputData);
144
0
  }
145
146
  // Ensure PGF version.
147
427
  byte mnb = readPgfMagicNumber(*io_);
148
149
427
  readPgfHeaderSize(*io_);
150
151
427
  uint32_t w = 0;
152
427
  uint32_t h = 0;
153
427
  DataBuf header = readPgfHeaderStructure(*io_, w, h);
154
155
427
  auto img = ImageFactory::create(ImageType::png);
156
157
427
  img->setExifData(exifData_);
158
427
  img->setIptcData(iptcData_);
159
427
  img->setXmpData(xmpData_);
160
427
  img->writeMetadata();
161
427
  size_t imgSize = img->io().size();
162
427
  DataBuf imgBuf = img->io().read(imgSize);
163
164
#ifdef EXIV2_DEBUG_MESSAGES
165
  std::cout << "Exiv2::PgfImage::doWriteMetadata: Creating image to host metadata (" << imgSize << " bytes)\n";
166
#endif
167
168
  //---------------------------------------------------------------
169
170
  // Write PGF Signature.
171
427
  if (outIo.write(pgfSignature.data(), 3) != 3)
172
0
    throw Error(ErrorCode::kerImageWriteFailed);
173
174
  // Write Magic number.
175
427
  if (outIo.putb(mnb) == EOF)
176
0
    throw Error(ErrorCode::kerImageWriteFailed);
177
178
  // Write new Header size.
179
427
  auto newHeaderSize = static_cast<uint32_t>(header.size() + imgSize);
180
427
  DataBuf buffer(4);
181
427
  std::memcpy(buffer.data(), &newHeaderSize, sizeof(uint32_t));
182
427
  byteSwap_(buffer, 0, bSwap_);
183
427
  if (outIo.write(buffer.c_data(), 4) != 4)
184
0
    throw Error(ErrorCode::kerImageWriteFailed);
185
186
#ifdef EXIV2_DEBUG_MESSAGES
187
  std::cout << "Exiv2::PgfImage: new PGF header size : " << newHeaderSize << " bytes\n";
188
189
  printf("%x\n", buffer.read_uint8(0));
190
  printf("%x\n", buffer.read_uint8(1));
191
  printf("%x\n", buffer.read_uint8(2));
192
  printf("%x\n", buffer.read_uint8(3));
193
#endif
194
195
  // Write Header data.
196
427
  if (outIo.write(header.c_data(), header.size()) != header.size())
197
0
    throw Error(ErrorCode::kerImageWriteFailed);
198
199
  // Write new metadata byte array.
200
427
  if (outIo.write(imgBuf.c_data(), imgBuf.size()) != imgBuf.size())
201
0
    throw Error(ErrorCode::kerImageWriteFailed);
202
203
  // Copy the rest of PGF image data.
204
205
427
  DataBuf buf(4096);
206
427
  size_t readSize = io_->read(buf.data(), buf.size());
207
1.25k
  while (readSize != 0) {
208
823
    if (outIo.write(buf.c_data(), readSize) != readSize)
209
0
      throw Error(ErrorCode::kerImageWriteFailed);
210
823
    readSize = io_->read(buf.data(), buf.size());
211
823
  }
212
427
  if (outIo.error())
213
0
    throw Error(ErrorCode::kerImageWriteFailed);
214
215
427
}  // PgfImage::doWriteMetadata
216
217
1.96k
byte PgfImage::readPgfMagicNumber(BasicIo& iIo) {
218
1.96k
  auto b = static_cast<byte>(iIo.getb());
219
1.96k
  if (iIo.error())
220
0
    throw Error(ErrorCode::kerFailedToReadImageData);
221
222
1.96k
  if (b < 0x36)  // 0x36 = '6'.
223
526
  {
224
    // Not right Magick version.
225
#ifdef EXIV2_DEBUG_MESSAGES
226
    std::cout << "Exiv2::PgfImage::readMetadata: wrong Magick number\n";
227
#endif
228
526
  }
229
230
1.96k
  return b;
231
1.96k
}  // PgfImage::readPgfMagicNumber
232
233
1.96k
size_t PgfImage::readPgfHeaderSize(BasicIo& iIo) const {
234
1.96k
  DataBuf buffer(4);
235
1.96k
  const size_t bufRead = iIo.read(buffer.data(), buffer.size());
236
1.96k
  if (iIo.error())
237
0
    throw Error(ErrorCode::kerFailedToReadImageData);
238
1.96k
  if (bufRead != buffer.size())
239
0
    throw Error(ErrorCode::kerInputDataReadFailed);
240
241
1.96k
  auto headerSize = static_cast<size_t>(byteSwap_(buffer, 0, bSwap_));
242
1.96k
  if (headerSize == 0)
243
40
    throw Error(ErrorCode::kerNoImageInInputData);
244
245
#ifdef EXIV2_DEBUG_MESSAGES
246
  std::cout << "Exiv2::PgfImage: PGF header size : " << headerSize << " bytes\n";
247
#endif
248
249
1.92k
  return headerSize;
250
1.96k
}
251
252
1.92k
DataBuf PgfImage::readPgfHeaderStructure(BasicIo& iIo, uint32_t& width, uint32_t& height) const {
253
1.92k
  DataBuf header(16);
254
1.92k
  size_t bufRead = iIo.read(header.data(), header.size());
255
1.92k
  if (iIo.error())
256
0
    throw Error(ErrorCode::kerFailedToReadImageData);
257
1.92k
  if (bufRead != header.size())
258
0
    throw Error(ErrorCode::kerInputDataReadFailed);
259
260
1.92k
  DataBuf work(header.data(), 8);  // don't disturb the binary data - doWriteMetadata reuses it
261
1.92k
  width = byteSwap_(work, 0, bSwap_);
262
1.92k
  height = byteSwap_(work, 4, bSwap_);
263
264
  /* NOTE: properties not yet used
265
  byte nLevels  = buffer.pData_[8];
266
  byte quality  = buffer.pData_[9];
267
  byte bpp      = buffer.pData_[10];
268
  byte channels = buffer.pData_[11];
269
  */
270
271
1.92k
  if (header.read_uint8(12) == 2)  // Indexed color image. We pass color table (256 * 3 bytes).
272
96
  {
273
96
    header.alloc(16 + (256 * 3));
274
275
96
    bufRead = iIo.read(header.data(16), 256 * 3);
276
96
    if (iIo.error())
277
0
      throw Error(ErrorCode::kerFailedToReadImageData);
278
96
    if (bufRead != 256 * 3)
279
85
      throw Error(ErrorCode::kerInputDataReadFailed);
280
96
  }
281
282
1.83k
  return header;
283
1.92k
}  // PgfImage::readPgfHeaderStructure
284
285
// *************************************************************************
286
// free functions
287
1.53k
Image::UniquePtr newPgfInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
288
1.53k
  auto image = std::make_unique<PgfImage>(std::move(io), params);
289
1.53k
  if (!image->good()) {
290
0
    return nullptr;
291
0
  }
292
1.53k
  return image;
293
1.53k
}
294
295
64.4k
bool isPgfType(BasicIo& iIo, bool advance) {
296
64.4k
  const int32_t len = 3;
297
64.4k
  std::array<byte, len> buf;
298
64.4k
  iIo.read(buf.data(), len);
299
64.4k
  if (iIo.error() || iIo.eof()) {
300
0
    return false;
301
0
  }
302
64.4k
  bool rc = buf == pgfSignature;
303
64.4k
  if (!advance || !rc) {
304
62.4k
    iIo.seek(-len, BasicIo::cur);
305
62.4k
  }
306
307
64.4k
  return rc;
308
64.4k
}
309
}  // namespace Exiv2