1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <zlib.h>
17
18 #include "data_buf.h"
19 #include "exif_metadata.h"
20 #include "image_log.h"
21 #include "media_errors.h"
22 #include "metadata_stream.h"
23 #include "png_exif_metadata_accessor.h"
24 #include "png_image_chunk_utils.h"
25 #include "tiff_parser.h"
26
27 #undef LOG_DOMAIN
28 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
29
30 #undef LOG_TAG
31 #define LOG_TAG "PngExifMetadataAccessor"
32
33 namespace OHOS {
34 namespace Media {
35 namespace {
36 constexpr auto PNG_CHUNK_DATA_MAX = 0x7fffffff;
37 constexpr auto PNG_CHUNK_IEND = "IEND";
38 constexpr auto PNG_CHUNK_IHDR = "IHDR";
39 constexpr auto PNG_CHUNK_TEXT = "tEXt";
40 constexpr auto PNG_CHUNK_ZTXT = "zTXt";
41 constexpr auto PNG_CHUNK_ITXT = "iTXt";
42 constexpr auto PNG_CHUNK_EXIF = "eXIf";
43 constexpr auto PNG_CHUNK_HEAD_SIZE = 8;
44 constexpr auto PNG_CHUNK_LENGTH_SIZE = 4;
45 constexpr auto PNG_CHUNK_TYPE_SIZE = 4;
46 constexpr auto PNG_CHUNK_CRC_SIZE = 4;
47 constexpr auto PNG_SIGN_SIZE = 8;
48 }
49
PngExifMetadataAccessor(std::shared_ptr<MetadataStream> & stream)50 PngExifMetadataAccessor::PngExifMetadataAccessor(std::shared_ptr<MetadataStream> &stream)
51 : AbstractExifMetadataAccessor(stream)
52 {}
53
~PngExifMetadataAccessor()54 PngExifMetadataAccessor::~PngExifMetadataAccessor() {}
55
IsPngType() const56 bool PngExifMetadataAccessor::IsPngType() const
57 {
58 if (imageStream_->IsEof()) {
59 return false;
60 }
61 const int32_t len = PNG_SIGN_SIZE;
62 byte buf[len];
63 if (imageStream_->Read(buf, len) == -1) {
64 return false;
65 }
66 if (imageStream_->IsEof()) {
67 return false;
68 }
69
70 return !memcmp(buf, pngSignature, PNG_SIGN_SIZE);
71 }
72
ReadChunk(DataBuf & buffer) const73 ssize_t PngExifMetadataAccessor::ReadChunk(DataBuf &buffer) const
74 {
75 return imageStream_->Read(buffer.Data(), buffer.Size());
76 }
77
FindTiffFromText(const DataBuf & data,const std::string chunkType,DataBuf & tiffData)78 bool PngExifMetadataAccessor::FindTiffFromText(const DataBuf &data, const std::string chunkType,
79 DataBuf &tiffData)
80 {
81 PngImageChunkUtils::TextChunkType txtType;
82 if (chunkType == PNG_CHUNK_TEXT) {
83 txtType = PngImageChunkUtils::tEXtChunk;
84 } else if (chunkType == PNG_CHUNK_ZTXT) {
85 txtType = PngImageChunkUtils::zTXtChunk;
86 } else if (chunkType == PNG_CHUNK_ITXT) {
87 txtType = PngImageChunkUtils::iTXtChunk;
88 } else {
89 return false;
90 }
91 bool isCompressed;
92 if (PngImageChunkUtils::ParseTextChunk(data, txtType, tiffData, isCompressed) != 0) {
93 return false;
94 }
95 if (isCompressed) {
96 isCompressed_ = isCompressed;
97 }
98 return true;
99 }
100
ProcessExifData(DataBuf & blob,std::string chunkType,uint32_t chunkLength)101 bool PngExifMetadataAccessor::ProcessExifData(DataBuf &blob, std::string chunkType, uint32_t chunkLength)
102 {
103 DataBuf chunkData(chunkLength);
104 if (chunkLength > 0) {
105 if (static_cast<size_t>(ReadChunk(chunkData)) != chunkData.Size()) {
106 IMAGE_LOGE("Failed to read chunk data. Expected size: %{public}zu", chunkData.Size());
107 return false;
108 }
109 }
110 if (chunkType != PNG_CHUNK_EXIF) {
111 return FindTiffFromText(chunkData, chunkType, blob);
112 }
113 blob = chunkData;
114 return true;
115 }
116
ReadBlob(DataBuf & blob)117 bool PngExifMetadataAccessor::ReadBlob(DataBuf &blob)
118 {
119 if (!imageStream_->IsOpen()) {
120 IMAGE_LOGE("The output image stream is not open");
121 return false;
122 }
123 imageStream_->Seek(0, SeekPos::BEGIN);
124
125 if (!IsPngType()) {
126 IMAGE_LOGE("The file is not a PNG file");
127 return false;
128 }
129
130 const size_t imgSize = static_cast<size_t>(imageStream_->GetSize());
131 DataBuf chunkHead(PNG_CHUNK_HEAD_SIZE);
132
133 while (!imageStream_->IsEof()) {
134 if (static_cast<size_t>(ReadChunk(chunkHead)) != chunkHead.Size()) {
135 IMAGE_LOGE("Failed to read chunk head. Expected size: %{public}zu", chunkHead.Size());
136 return false;
137 }
138 uint32_t chunkLength = chunkHead.ReadUInt32(0, bigEndian);
139 if (chunkLength > imgSize - imageStream_->Tell()) {
140 IMAGE_LOGE("Chunk length is larger than the remaining image size");
141 return false;
142 }
143 std::string chunkType(reinterpret_cast<const char *>(chunkHead.CData(PNG_CHUNK_LENGTH_SIZE)),
144 PNG_CHUNK_TYPE_SIZE);
145 if (chunkType == PNG_CHUNK_IEND) {
146 return false;
147 }
148 if (chunkType == PNG_CHUNK_TEXT || chunkType == PNG_CHUNK_ZTXT || chunkType == PNG_CHUNK_EXIF ||
149 chunkType == PNG_CHUNK_ITXT) {
150 if (ProcessExifData(blob, chunkType, chunkLength)) {
151 break;
152 }
153 chunkLength = 0;
154 }
155 imageStream_->Seek(chunkLength + PNG_CHUNK_CRC_SIZE, CURRENT);
156 if (imageStream_->IsEof()) {
157 IMAGE_LOGE("Failed to read the file");
158 return false;
159 }
160 }
161 tiffOffset_ = imageStream_->Tell() - static_cast<long>(blob.Size());
162 return true;
163 }
164
Read()165 uint32_t PngExifMetadataAccessor::Read()
166 {
167 DataBuf tiffBuf;
168 if (!ReadBlob(tiffBuf)) {
169 IMAGE_LOGD("Failed to read the blob.");
170 return ERR_IMAGE_SOURCE_DATA;
171 }
172 ExifData *exifData;
173 size_t byteOrderPos = TiffParser::FindTiffPos(tiffBuf);
174 if (byteOrderPos == std::numeric_limits<size_t>::max()) {
175 IMAGE_LOGE("Cannot find TIFF byte order in Exif metadata.");
176 return ERR_IMAGE_SOURCE_DATA;
177 }
178 TiffParser::Decode(tiffBuf.CData(), tiffBuf.Size(), &exifData);
179 if (exifData == nullptr) {
180 IMAGE_LOGE("Failed to decode TIFF buffer.");
181 return ERR_EXIF_DECODE_FAILED;
182 }
183
184 exifMetadata_ = std::make_shared<OHOS::Media::ExifMetadata>(exifData);
185 return SUCCESS;
186 }
187
GetExifEncodedBlob(uint8_t ** dataBlob,uint32_t & size)188 bool PngExifMetadataAccessor::GetExifEncodedBlob(uint8_t **dataBlob, uint32_t &size)
189 {
190 if (this->Get() == nullptr) {
191 IMAGE_LOGE("Exif metadata empty");
192 return false;
193 }
194
195 ExifData *exifData = this->Get()->GetExifData();
196 TiffParser::Encode(dataBlob, size, exifData);
197
198 if (dataBlob == nullptr || *dataBlob == nullptr) {
199 IMAGE_LOGE("Encode Jpeg data failed");
200 return false;
201 }
202 DataBuf blobBuf(*dataBlob, size);
203 size_t byteOrderPos = TiffParser::FindTiffPos(blobBuf);
204 if (byteOrderPos == std::numeric_limits<size_t>::max()) {
205 IMAGE_LOGE("Failed to Encode Exif metadata: cannot find tiff byte order");
206 return false;
207 }
208 return ((size > 0) && (size <= PNG_CHUNK_DATA_MAX));
209 }
210
WriteData(BufferMetadataStream & bufStream,uint8_t * data,uint32_t size)211 bool PngExifMetadataAccessor::WriteData(BufferMetadataStream &bufStream, uint8_t *data, uint32_t size)
212 {
213 if (bufStream.Write(data, size) != size) {
214 IMAGE_LOGE("Write the bufStream failed");
215 return false;
216 }
217 return true;
218 }
219
WriteExifData(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size,DataBuf & chunkBuf,std::string chunkType)220 bool PngExifMetadataAccessor::WriteExifData(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size,
221 DataBuf &chunkBuf, std::string chunkType)
222 {
223 if ((dataBlob == nullptr) || (size == 0)) {
224 return false;
225 }
226 if (chunkType == PNG_CHUNK_EXIF) {
227 return true;
228 }
229 if (chunkType == PNG_CHUNK_IHDR) {
230 if (!WriteData(bufStream, chunkBuf.Data(), chunkBuf.Size())) {
231 return false;
232 }
233
234 byte length[PNG_CHUNK_LENGTH_SIZE];
235 UL2Data(length, size, bigEndian);
236 uint8_t typeExif[] = "eXIf";
237 uint32_t tmp = crc32(0L, Z_NULL, 0);
238 tmp = crc32(tmp, typeExif, PNG_CHUNK_CRC_SIZE);
239 tmp = crc32(tmp, dataBlob, size);
240 byte crc[PNG_CHUNK_CRC_SIZE];
241 UL2Data(crc, tmp, bigEndian);
242 if (!(WriteData(bufStream, length, PNG_CHUNK_LENGTH_SIZE) &&
243 WriteData(bufStream, typeExif, PNG_CHUNK_TYPE_SIZE) &&
244 WriteData(bufStream, dataBlob, size) &&
245 WriteData(bufStream, crc, PNG_CHUNK_CRC_SIZE))) {
246 return false;
247 }
248 } else if (chunkType == PNG_CHUNK_TEXT || chunkType == PNG_CHUNK_ZTXT || chunkType == PNG_CHUNK_ITXT) {
249 if (PngImageChunkUtils::FindExifFromTxt(chunkBuf)) {
250 return true;
251 }
252
253 return WriteData(bufStream, chunkBuf.Data(), chunkBuf.Size());
254 }
255 return true;
256 }
257
UpdateExifMetadata(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)258 bool PngExifMetadataAccessor::UpdateExifMetadata(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
259 {
260 const size_t imgSize = static_cast<size_t>(imageStream_->GetSize());
261 DataBuf chunkHead(PNG_CHUNK_HEAD_SIZE);
262
263 if (!WriteData(bufStream, pngSignature, PNG_SIGN_SIZE)) {
264 return false;
265 }
266
267 while (!imageStream_->IsEof()) {
268 if (static_cast<size_t>(ReadChunk(chunkHead)) != chunkHead.Size()) {
269 IMAGE_LOGE("Read chunk head error.");
270 return false;
271 }
272
273 uint32_t chunkLength = chunkHead.ReadUInt32(0, bigEndian);
274 if (chunkLength > imgSize - imageStream_->Tell()) {
275 IMAGE_LOGE("Read chunk length error.");
276 return false;
277 }
278
279 DataBuf chunkBuf(PNG_CHUNK_HEAD_SIZE + chunkLength + PNG_CHUNK_CRC_SIZE);
280 std::copy_n(chunkHead.Begin(), PNG_CHUNK_HEAD_SIZE, chunkBuf.Begin());
281
282 ssize_t bufLength = imageStream_->Read(chunkBuf.Data(PNG_CHUNK_HEAD_SIZE), chunkLength + PNG_CHUNK_CRC_SIZE);
283 if (bufLength != chunkLength + PNG_CHUNK_CRC_SIZE) {
284 IMAGE_LOGE("Read chunk head error.");
285 return false;
286 }
287 std::string chunkType(reinterpret_cast<const char*>(chunkHead.CData(PNG_CHUNK_LENGTH_SIZE)),
288 PNG_CHUNK_TYPE_SIZE);
289 if (chunkType == PNG_CHUNK_IEND) {
290 return WriteData(bufStream, chunkBuf.Data(), chunkBuf.Size());
291 }
292 if (chunkType == PNG_CHUNK_EXIF || chunkType == PNG_CHUNK_IHDR || chunkType == PNG_CHUNK_TEXT ||
293 chunkType == PNG_CHUNK_ZTXT || chunkType == PNG_CHUNK_ITXT) {
294 if (!WriteExifData(bufStream, dataBlob, size, chunkBuf, chunkType)) {
295 return false;
296 }
297 } else {
298 if (!WriteData(bufStream, chunkBuf.Data(), chunkBuf.Size())) {
299 return false;
300 }
301 }
302 }
303 return false;
304 }
305
UpdateData(uint8_t * dataBlob,uint32_t size)306 uint32_t PngExifMetadataAccessor::UpdateData(uint8_t *dataBlob, uint32_t size)
307 {
308 BufferMetadataStream tmpBufStream;
309 if (!tmpBufStream.Open(OpenMode::ReadWrite)) {
310 IMAGE_LOGE("Image temp stream open failed");
311 return ERR_IMAGE_SOURCE_DATA;
312 }
313
314 if (!UpdateExifMetadata(tmpBufStream, dataBlob, size)) {
315 IMAGE_LOGE("Image temp stream write failed");
316 return ERROR;
317 }
318
319 imageStream_->Seek(0, SeekPos::BEGIN);
320 if (!imageStream_->CopyFrom(tmpBufStream)) {
321 IMAGE_LOGE("Copy from temp stream failed");
322 return ERR_MEDIA_INVALID_OPERATION;
323 }
324 return SUCCESS;
325 }
326
Write()327 uint32_t PngExifMetadataAccessor::Write()
328 {
329 uint8_t *dataBlob = nullptr;
330 uint32_t size = 0;
331
332 if (!imageStream_->IsOpen()) {
333 IMAGE_LOGE("Output image stream not open");
334 return ERR_IMAGE_SOURCE_DATA;
335 }
336 imageStream_->Seek(0, SeekPos::BEGIN);
337 if (!IsPngType()) {
338 IMAGE_LOGE("Is not a PNG file.");
339 return ERR_IMAGE_SOURCE_DATA;
340 }
341
342 if (!GetExifEncodedBlob(&dataBlob, size)) {
343 IMAGE_LOGE("Encode Metadata failed");
344 return ERR_MEDIA_VALUE_INVALID;
345 }
346
347 uint32_t result = UpdateData(dataBlob, size);
348
349 if (dataBlob != nullptr) {
350 free(dataBlob);
351 dataBlob = nullptr;
352 }
353
354 return result;
355 }
356
WriteBlob(DataBuf & blob)357 uint32_t PngExifMetadataAccessor::WriteBlob(DataBuf &blob)
358 {
359 byte *dataBlob = nullptr;
360 uint32_t size = 0;
361
362 imageStream_->Seek(0, SeekPos::BEGIN);
363 if (!IsPngType()) {
364 IMAGE_LOGE("Is not a PNG file.");
365 return ERR_IMAGE_SOURCE_DATA;
366 }
367
368 if (blob.Empty()) {
369 IMAGE_LOGE("Image exif blob data empty");
370 return ERR_MEDIA_VALUE_INVALID;
371 }
372
373 size_t byteOrderPos = TiffParser::FindTiffPos(blob);
374 if (byteOrderPos == std::numeric_limits<size_t>::max()) {
375 IMAGE_LOGE("Failed to checkout Exif metadata: cannot find tiff byte order");
376 return ERR_MEDIA_VALUE_INVALID;
377 }
378
379 dataBlob = const_cast<byte *>(blob.CData());
380 size = blob.Size();
381
382 return UpdateData(dataBlob, size);
383 }
384
GetFilterArea(const std::vector<std::string> & exifKeys,std::vector<std::pair<uint32_t,uint32_t>> & ranges)385 uint32_t PngExifMetadataAccessor::GetFilterArea(const std::vector<std::string> &exifKeys,
386 std::vector<std::pair<uint32_t, uint32_t>> &ranges)
387 {
388 uint32_t ret = Read();
389 if (ret != SUCCESS) {
390 IMAGE_LOGD("Failed to read the exif info.");
391 return E_NO_EXIF_TAG;
392 }
393 if (isCompressed_) {
394 IMAGE_LOGD("This png is compressed.");
395 return E_NO_EXIF_TAG;
396 }
397 exifMetadata_->GetFilterArea(exifKeys, ranges);
398 if (ranges.empty()) {
399 return E_NO_EXIF_TAG;
400 }
401 for (auto& range : ranges) {
402 range.first += static_cast<uint32_t>(GetTiffOffset());
403 }
404 return SUCCESS;
405 }
406 } // namespace Media
407 } // namespace OHOS
408