/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 |