Coverage Report

Created: 2026-05-31 06:50

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, bool /*create*/) :
44
6.70k
    Image(ImageType::tiff, mdExif | mdIptc | mdXmp, std::move(io)) {
45
6.70k
}  // 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
6.49k
  bool operator==(int c) const {
54
6.49k
    return comp == c;
55
6.49k
  }
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
6.58k
std::string TiffImage::mimeType() const {
62
6.58k
  if (!mimeType_.empty())
63
0
    return mimeType_;
64
65
6.58k
  mimeType_ = std::string("image/tiff");
66
6.58k
  std::string key = "Exif." + primaryGroup() + ".Compression";
67
6.58k
  auto md = exifData_.findKey(ExifKey(key));
68
6.58k
  if (md != exifData_.end() && md->count() > 0) {
69
1.09k
    auto mt = Exiv2::find(mimeTypeList, static_cast<int>(md->toInt64()));
70
1.09k
    if (mt)
71
37
      mimeType_ = mt->type;
72
1.09k
  }
73
6.58k
  return mimeType_;
74
6.58k
}
75
76
19.7k
std::string TiffImage::primaryGroup() const {
77
19.7k
  if (!primaryGroup_.empty())
78
13.1k
    return primaryGroup_;
79
80
6.58k
  static constexpr auto keys = std::array{
81
6.58k
      "Exif.Image.NewSubfileType",     "Exif.SubImage1.NewSubfileType", "Exif.SubImage2.NewSubfileType",
82
6.58k
      "Exif.SubImage3.NewSubfileType", "Exif.SubImage4.NewSubfileType", "Exif.SubImage5.NewSubfileType",
83
6.58k
      "Exif.SubImage6.NewSubfileType", "Exif.SubImage7.NewSubfileType", "Exif.SubImage8.NewSubfileType",
84
6.58k
      "Exif.SubImage9.NewSubfileType",
85
6.58k
  };
86
  // Find the group of the primary image, default to "Image"
87
6.58k
  primaryGroup_ = std::string("Image");
88
65.5k
  for (auto i : keys) {
89
65.5k
    auto md = exifData_.findKey(ExifKey(i));
90
    // Is it the primary image?
91
65.5k
    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
39
      primaryGroup_ = md->groupName();
94
39
      std::string key = "Exif." + primaryGroup_ + ".JPEGInterchangeFormat";
95
39
      if (exifData_.findKey(ExifKey(key)) == exifData_.end())
96
27
        break;
97
39
    }
98
65.5k
  }
99
6.58k
  return primaryGroup_;
100
19.7k
}
101
102
6.58k
uint32_t TiffImage::pixelWidth() const {
103
6.58k
  if (pixelWidthPrimary_ != 0) {
104
0
    return pixelWidthPrimary_;
105
0
  }
106
107
6.58k
  ExifKey key(std::string("Exif.") + primaryGroup() + std::string(".ImageWidth"));
108
6.58k
  auto imageWidth = exifData_.findKey(key);
109
6.58k
  if (imageWidth != exifData_.end() && imageWidth->count() > 0) {
110
525
    pixelWidthPrimary_ = imageWidth->toUint32();
111
525
  }
112
6.58k
  return pixelWidthPrimary_;
113
6.58k
}
114
115
6.58k
uint32_t TiffImage::pixelHeight() const {
116
6.58k
  if (pixelHeightPrimary_ != 0) {
117
0
    return pixelHeightPrimary_;
118
0
  }
119
120
6.58k
  ExifKey key(std::string("Exif.") + primaryGroup() + std::string(".ImageLength"));
121
6.58k
  auto imageHeight = exifData_.findKey(key);
122
6.58k
  if (imageHeight != exifData_.end() && imageHeight->count() > 0) {
123
171
    pixelHeightPrimary_ = imageHeight->toUint32();
124
171
  }
125
6.58k
  return pixelHeightPrimary_;
126
6.58k
}
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
6.62k
void TiffImage::readMetadata() {
134
#ifdef EXIV2_DEBUG_MESSAGES
135
  std::cerr << "Reading TIFF file " << io_->path() << "\n";
136
#endif
137
6.62k
  if (io_->open() != 0) {
138
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
139
0
  }
140
141
6.62k
  IoCloser closer(*io_);
142
  // Ensure that this is the correct image type
143
6.62k
  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
6.62k
  clearMetadata();
149
150
6.62k
  ByteOrder bo = TiffParser::decode(exifData_, iptcData_, xmpData_, io_->mmap(), io_->size());
151
6.62k
  setByteOrder(bo);
152
153
  // read profile from the metadata
154
6.62k
  Exiv2::ExifKey key("Exif.Image.InterColorProfile");
155
6.62k
  auto pos = exifData_.findKey(key);
156
6.62k
  if (pos != exifData_.end()) {
157
16
    size_t size = pos->count() * pos->typeSize();
158
16
    if (size == 0) {
159
10
      throw Error(ErrorCode::kerFailedToReadImageData);
160
10
    }
161
6
    iccProfile_.alloc(size);
162
6
    pos->copy(iccProfile_.data(), bo);
163
6
  }
164
6.62k
}
165
166
0
void TiffImage::writeMetadata() {
167
#ifdef EXIV2_DEBUG_MESSAGES
168
  std::cerr << "Writing TIFF file " << io_->path() << "\n";
169
#endif
170
0
  ByteOrder bo = byteOrder();
171
0
  const byte* pData = nullptr;
172
0
  size_t size = 0;
173
0
  IoCloser closer(*io_);
174
  // Ensure that this is the correct image type
175
0
  if (io_->open() == 0 && isTiffType(*io_, false)) {
176
0
    pData = io_->mmap(true);
177
0
    size = io_->size();
178
0
    TiffHeader tiffHeader;
179
0
    if (0 == tiffHeader.read(pData, 8)) {
180
0
      bo = tiffHeader.byteOrder();
181
0
    }
182
0
  }
183
0
  if (bo == invalidByteOrder) {
184
0
    bo = littleEndian;
185
0
  }
186
0
  setByteOrder(bo);
187
188
  // fixup ICC profile
189
0
  Exiv2::ExifKey key("Exif.Image.InterColorProfile");
190
0
  auto pos = exifData_.findKey(key);
191
0
  bool found = pos != exifData_.end();
192
0
  if (iccProfileDefined()) {
193
0
    Exiv2::DataValue value(iccProfile_.c_data(), iccProfile_.size());
194
0
    if (found)
195
0
      pos->setValue(&value);
196
0
    else
197
0
      exifData_.add(key, &value);
198
0
  } else {
199
0
    if (found)
200
0
      exifData_.erase(pos);
201
0
  }
202
203
  // set usePacket to influence TiffEncoder::encodeXmp() called by TiffVisitor.encode()
204
0
  xmpData().usePacket(writeXmpFromPacket());
205
206
0
  TiffParser::encode(*io_, pData, size, bo, exifData_, iptcData_, xmpData_);  // may throw
207
0
}  // TiffImage::writeMetadata
208
209
9.35k
ByteOrder TiffParser::decode(ExifData& exifData, IptcData& iptcData, XmpData& xmpData, const byte* pData, size_t size) {
210
9.35k
  uint32_t root = Tag::root;
211
212
  // #1402  Fujifilm RAF. Change root when parsing embedded tiff
213
9.35k
  Exiv2::ExifKey key("Exif.Image.Make");
214
9.35k
  if (exifData.findKey(key) != exifData.end() && exifData.findKey(key)->toString() == "FUJIFILM") {
215
0
    root = Tag::fuji;
216
0
  }
217
218
9.35k
  return TiffParserWorker::decode(exifData, iptcData, xmpData, pData, size, root, TiffMapping::findDecoder);
219
9.35k
}  // TiffParser::decode
220
221
WriteMethod TiffParser::encode(BasicIo& io, const byte* pData, size_t size, ByteOrder byteOrder, ExifData& exifData,
222
2.94k
                               const IptcData& iptcData, const XmpData& xmpData) {
223
  // Delete IFDs which do not occur in TIFF images
224
2.94k
  static constexpr auto filteredIfds = std::array{
225
2.94k
      IfdId::panaRawId,
226
2.94k
  };
227
2.94k
  for (auto filteredIfd : filteredIfds) {
228
#ifdef EXIV2_DEBUG_MESSAGES
229
    std::cerr << "Warning: Exif IFD " << filteredIfd << " not encoded\n";
230
#endif
231
2.94k
    exifData.erase(std::remove_if(exifData.begin(), exifData.end(), FindExifdatum(filteredIfd)), exifData.end());
232
2.94k
  }
233
234
2.94k
  TiffHeader header(byteOrder);
235
2.94k
  return TiffParserWorker::encode(io, pData, size, exifData, iptcData, xmpData, Tag::root, TiffMapping::findEncoder,
236
2.94k
                                  &header, nullptr);
237
2.94k
}  // TiffParser::encode
238
239
// *************************************************************************
240
// free functions
241
6.62k
Image::UniquePtr newTiffInstance(BasicIo::UniquePtr io, bool create) {
242
6.62k
  auto image = std::make_unique<TiffImage>(std::move(io), create);
243
6.62k
  if (!image->good()) {
244
0
    return nullptr;
245
0
  }
246
6.62k
  return image;
247
6.62k
}
248
249
141k
bool isTiffType(BasicIo& iIo, bool advance) {
250
141k
  const int32_t len = 8;
251
141k
  byte buf[len];
252
141k
  iIo.read(buf, len);
253
141k
  if (iIo.error() || iIo.eof()) {
254
1.47k
    return false;
255
1.47k
  }
256
140k
  TiffHeader tiffHeader;
257
140k
  bool rc = tiffHeader.read(buf, len);
258
140k
  if (!advance || !rc) {
259
140k
    iIo.seek(-len, BasicIo::cur);
260
140k
  }
261
140k
  return rc;
262
141k
}
263
264
0
void TiffImage::printStructure(std::ostream& out, Exiv2::PrintStructureOption option, size_t depth) {
265
0
  if (io_->open() != 0)
266
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
267
  // Ensure that this is the correct image type
268
0
  if (imageType() == ImageType::none && !isTiffType(*io_, false)) {
269
0
    if (io_->error() || io_->eof())
270
0
      throw Error(ErrorCode::kerFailedToReadImageData);
271
0
    throw Error(ErrorCode::kerNotAJpeg);
272
0
  }
273
274
0
  io_->seek(0, BasicIo::beg);
275
276
0
  printTiffStructure(io(), out, option, depth);
277
0
}
278
279
}  // namespace Exiv2