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