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