Coverage Report

Created: 2026-08-25 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/tiffimage.cpp
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
// included header files
4
#include "tiffimage.hpp"
5
6
#include "basicio.hpp"
7
#include "config.h"
8
#include "error.hpp"
9
#include "futils.hpp"
10
#include "image.hpp"
11
#include "tags.hpp"
12
#include "tiffcomposite_int.hpp"
13
#include "tiffimage_int.hpp"
14
#include "types.hpp"
15
#include "value.hpp"
16
17
#include <array>
18
#include <iostream>
19
20
/* --------------------------------------------------------------------------
21
22
   Todo:
23
24
   + CR2 Makernotes don't seem to have a next pointer but Canon Jpeg Makernotes
25
     do. What a mess. (That'll become an issue when it comes to writing to CR2)
26
   + Sony makernotes in RAW files do not seem to have header like those in Jpegs.
27
     And maybe no next pointer either.
28
29
   in crwimage.* :
30
31
   + Fix CiffHeader according to TiffHeader
32
   + Combine Error(ErrorCode::kerNotAJpeg) and Error(kerNotACrwImage), add format argument %1
33
   + Search crwimage for todos, fix writeMetadata comment
34
   + rename loadStack to getPath for consistency
35
36
   -------------------------------------------------------------------------- */
37
38
// *****************************************************************************
39
// class member definitions
40
namespace Exiv2 {
41
using namespace Internal;
42
43
TiffImage::TiffImage(BasicIo::UniquePtr io, const ImageCtorParams& params) :
44
1.27k
    Image(ImageType::tiff, mdExif | mdIptc | mdXmp, std::move(io), params) {
45
1.27k
}  // TiffImage::TiffImage
46
47
//! List of TIFF compression to MIME type mappings
48
49
constexpr struct mimeType {
50
  int comp;
51
  const char* type;
52
53
0
  bool operator==(int c) const {
54
0
    return comp == c;
55
0
  }
56
} mimeTypeList[] = {
57
    {32767, "image/x-sony-arw"},  {32769, "image/x-epson-erf"}, {32770, "image/x-samsung-srw"},
58
    {34713, "image/x-nikon-nef"}, {65000, "image/x-kodak-dcr"}, {65535, "image/x-pentax-pef"},
59
};
60
61
0
std::string TiffImage::mimeType() const {
62
0
  if (!mimeType_.empty())
63
0
    return mimeType_;
64
65
0
  mimeType_ = std::string("image/tiff");
66
0
  std::string key = "Exif." + primaryGroup() + ".Compression";
67
0
  auto md = exifData_.findKey(ExifKey(key));
68
0
  if (md != exifData_.end() && md->count() > 0) {
69
0
    auto mt = Exiv2::find(mimeTypeList, static_cast<int>(md->toInt64()));
70
0
    if (mt)
71
0
      mimeType_ = mt->type;
72
0
  }
73
0
  return mimeType_;
74
0
}
75
76
2.45k
std::string TiffImage::primaryGroup() const {
77
2.45k
  if (!primaryGroup_.empty())
78
1.22k
    return primaryGroup_;
79
80
1.22k
  static constexpr auto keys = std::array{
81
1.22k
      "Exif.Image.NewSubfileType",     "Exif.SubImage1.NewSubfileType", "Exif.SubImage2.NewSubfileType",
82
1.22k
      "Exif.SubImage3.NewSubfileType", "Exif.SubImage4.NewSubfileType", "Exif.SubImage5.NewSubfileType",
83
1.22k
      "Exif.SubImage6.NewSubfileType", "Exif.SubImage7.NewSubfileType", "Exif.SubImage8.NewSubfileType",
84
1.22k
      "Exif.SubImage9.NewSubfileType",
85
1.22k
  };
86
  // Find the group of the primary image, default to "Image"
87
1.22k
  primaryGroup_ = std::string("Image");
88
12.2k
  for (auto i : keys) {
89
12.2k
    auto md = exifData_.findKey(ExifKey(i));
90
    // Is it the primary image?
91
12.2k
    if (md != exifData_.end() && md->count() > 0 && md->toInt64() == 0) {
92
      // Sometimes there is a JPEG primary image; that's not our first choice
93
9
      primaryGroup_ = md->groupName();
94
9
      std::string key = "Exif." + primaryGroup_ + ".JPEGInterchangeFormat";
95
9
      if (exifData_.findKey(ExifKey(key)) == exifData_.end())
96
5
        break;
97
9
    }
98
12.2k
  }
99
1.22k
  return primaryGroup_;
100
2.45k
}
101
102
1.34k
uint32_t TiffImage::pixelWidth() const {
103
1.34k
  if (pixelWidthPrimary_ != 0) {
104
115
    return pixelWidthPrimary_;
105
115
  }
106
107
1.22k
  ExifKey key(std::string("Exif.") + primaryGroup() + std::string(".ImageWidth"));
108
1.22k
  auto imageWidth = exifData_.findKey(key);
109
1.22k
  if (imageWidth != exifData_.end() && imageWidth->count() > 0) {
110
156
    pixelWidthPrimary_ = imageWidth->toUint32();
111
156
  }
112
1.22k
  return pixelWidthPrimary_;
113
1.34k
}
114
115
1.31k
uint32_t TiffImage::pixelHeight() const {
116
1.31k
  if (pixelHeightPrimary_ != 0) {
117
92
    return pixelHeightPrimary_;
118
92
  }
119
120
1.22k
  ExifKey key(std::string("Exif.") + primaryGroup() + std::string(".ImageLength"));
121
1.22k
  auto imageHeight = exifData_.findKey(key);
122
1.22k
  if (imageHeight != exifData_.end() && imageHeight->count() > 0) {
123
101
    pixelHeightPrimary_ = imageHeight->toUint32();
124
101
  }
125
1.22k
  return pixelHeightPrimary_;
126
1.31k
}
127
128
0
void TiffImage::setComment(const std::string&) {
129
  // not supported
130
0
  throw(Error(ErrorCode::kerInvalidSettingForImage, "Image comment", "TIFF"));
131
0
}
132
133
1.23k
void TiffImage::readMetadata() {
134
#ifdef EXIV2_DEBUG_MESSAGES
135
  std::cerr << "Reading TIFF file " << io_->path() << "\n";
136
#endif
137
1.23k
  if (io_->open() != 0) {
138
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
139
0
  }
140
141
1.23k
  IoCloser closer(*io_);
142
  // Ensure that this is the correct image type
143
1.23k
  if (!isTiffType(*io_, false)) {
144
0
    if (io_->error() || io_->eof())
145
0
      throw Error(ErrorCode::kerFailedToReadImageData);
146
0
    throw Error(ErrorCode::kerNotAnImage, "TIFF");
147
0
  }
148
1.23k
  clearMetadata();
149
150
1.23k
  const DecodeParams dp(max_recursion_depth_);
151
1.23k
  ByteOrder bo = TiffParser::decode(exifData_, iptcData_, xmpData_, io_->mmap(), io_->size(), dp);
152
1.23k
  setByteOrder(bo);
153
154
  // read profile from the metadata
155
1.23k
  Exiv2::ExifKey key("Exif.Image.InterColorProfile");
156
1.23k
  auto pos = exifData_.findKey(key);
157
1.23k
  if (pos != exifData_.end()) {
158
10
    size_t size = pos->count() * pos->typeSize();
159
10
    if (size == 0) {
160
3
      throw Error(ErrorCode::kerFailedToReadImageData);
161
3
    }
162
7
    iccProfile_.alloc(size);
163
7
    pos->copy(iccProfile_.data(), bo);
164
7
  }
165
1.23k
}
166
167
0
void TiffImage::writeMetadata() {
168
#ifdef EXIV2_DEBUG_MESSAGES
169
  std::cerr << "Writing TIFF file " << io_->path() << "\n";
170
#endif
171
0
  ByteOrder bo = byteOrder();
172
0
  const byte* pData = nullptr;
173
0
  size_t size = 0;
174
0
  IoCloser closer(*io_);
175
  // Ensure that this is the correct image type
176
0
  if (io_->open() == 0 && isTiffType(*io_, false)) {
177
0
    pData = io_->mmap(true);
178
0
    size = io_->size();
179
0
    TiffHeader tiffHeader;
180
0
    if (0 == tiffHeader.read(pData, 8)) {
181
0
      bo = tiffHeader.byteOrder();
182
0
    }
183
0
  }
184
0
  if (bo == invalidByteOrder) {
185
0
    bo = littleEndian;
186
0
  }
187
0
  setByteOrder(bo);
188
189
  // fixup ICC profile
190
0
  Exiv2::ExifKey key("Exif.Image.InterColorProfile");
191
0
  auto pos = exifData_.findKey(key);
192
0
  bool found = pos != exifData_.end();
193
0
  if (iccProfileDefined()) {
194
0
    Exiv2::DataValue value(iccProfile_.c_data(), iccProfile_.size());
195
0
    if (found)
196
0
      pos->setValue(&value);
197
0
    else
198
0
      exifData_.add(key, &value);
199
0
  } else {
200
0
    if (found)
201
0
      exifData_.erase(pos);
202
0
  }
203
204
  // set usePacket to influence TiffEncoder::encodeXmp() called by TiffVisitor.encode()
205
0
  xmpData().usePacket(writeXmpFromPacket());
206
207
0
  TiffParser::encode(*io_, pData, size, bo, exifData_, iptcData_, xmpData_);  // may throw
208
0
}  // TiffImage::writeMetadata
209
210
ByteOrder TiffParser::decode(ExifData& exifData, IptcData& iptcData, XmpData& xmpData, const byte* pData, size_t size,
211
25.5k
                             const DecodeParams& dp) {
212
25.5k
  uint32_t root = Tag::root;
213
214
  // #1402  Fujifilm RAF. Change root when parsing embedded tiff
215
25.5k
  Exiv2::ExifKey key("Exif.Image.Make");
216
25.5k
  if (exifData.findKey(key) != exifData.end() && exifData.findKey(key)->toString() == "FUJIFILM") {
217
0
    root = Tag::fuji;
218
0
  }
219
220
25.5k
  return TiffParserWorker::decode(exifData, iptcData, xmpData, pData, size, root, TiffMapping::findDecoder, dp);
221
25.5k
}  // TiffParser::decode
222
223
WriteMethod TiffParser::encode(BasicIo& io, const byte* pData, size_t size, ByteOrder byteOrder, ExifData& exifData,
224
0
                               const IptcData& iptcData, const XmpData& xmpData) {
225
  // Delete IFDs which do not occur in TIFF images
226
0
  static constexpr auto filteredIfds = std::array{
227
0
      IfdId::panaRawId,
228
0
  };
229
0
  for (auto filteredIfd : filteredIfds) {
230
#ifdef EXIV2_DEBUG_MESSAGES
231
    std::cerr << "Warning: Exif IFD " << filteredIfd << " not encoded\n";
232
#endif
233
0
    exifData.erase(std::remove_if(exifData.begin(), exifData.end(), FindExifdatum(filteredIfd)), exifData.end());
234
0
  }
235
236
0
  TiffHeader header(byteOrder);
237
0
  return TiffParserWorker::encode(io, pData, size, exifData, iptcData, xmpData, Tag::root, TiffMapping::findEncoder,
238
0
                                  &header, nullptr);
239
0
}  // TiffParser::encode
240
241
// *************************************************************************
242
// free functions
243
1.23k
Image::UniquePtr newTiffInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
244
1.23k
  auto image = std::make_unique<TiffImage>(std::move(io), params);
245
1.23k
  if (!image->good()) {
246
0
    return nullptr;
247
0
  }
248
1.23k
  return image;
249
1.23k
}
250
251
32.1k
bool isTiffType(BasicIo& iIo, bool advance) {
252
32.1k
  const int32_t len = 8;
253
32.1k
  byte buf[len];
254
32.1k
  iIo.read(buf, len);
255
32.1k
  if (iIo.error() || iIo.eof()) {
256
0
    return false;
257
0
  }
258
32.1k
  TiffHeader tiffHeader;
259
32.1k
  bool rc = tiffHeader.read(buf, len);
260
32.1k
  if (!advance || !rc) {
261
32.1k
    iIo.seek(-len, BasicIo::cur);
262
32.1k
  }
263
32.1k
  return rc;
264
32.1k
}
265
266
0
void TiffImage::printStructure(std::ostream& out, Exiv2::PrintStructureOption option, size_t depth) {
267
0
  if (io_->open() != 0)
268
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
269
  // Ensure that this is the correct image type
270
0
  if (imageType() == ImageType::none && !isTiffType(*io_, false)) {
271
0
    if (io_->error() || io_->eof())
272
0
      throw Error(ErrorCode::kerFailedToReadImageData);
273
0
    throw Error(ErrorCode::kerNotAJpeg);
274
0
  }
275
276
0
  io_->seek(0, BasicIo::beg);
277
278
0
  printTiffStructure(io(), out, option, depth);
279
0
}
280
281
}  // namespace Exiv2