1 // Copyright 2015 Google Inc.
2 //
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
17 #include "src/tiff_parser.h"
18
19 #include <cstring>
20 #include <limits>
21 #include <numeric>
22
23 #include "src/tiff_directory/tiff_directory.h"
24
25 namespace piex {
26 namespace {
27
28 using tiff_directory::Endian;
29 using tiff_directory::Rational;
30 using tiff_directory::SRational;
31 using tiff_directory::SizeOfType;
32 using tiff_directory::TIFF_TYPE_LONG;
33 using tiff_directory::TIFF_TYPE_UNDEFINED;
34 using tiff_directory::TiffDirectory;
35 using tiff_directory::kBigEndian;
36 using tiff_directory::kLittleEndian;
37
38 // Specifies all tags that might be of interest to parse JPEG data.
39 const std::uint32_t kStartOfFrame = 0xFFC0;
40 const std::uint32_t kStartOfImage = 0xFFD8;
41 const std::uint32_t kStartOfScan = 0xFFDA;
42
GetFullDimension16(const TiffDirectory & tiff_directory,std::uint16_t * width,std::uint16_t * height)43 bool GetFullDimension16(const TiffDirectory& tiff_directory,
44 std::uint16_t* width, std::uint16_t* height) {
45 std::uint32_t tmp_width = 0;
46 std::uint32_t tmp_height = 0;
47 if (!GetFullDimension32(tiff_directory, &tmp_width, &tmp_height) ||
48 tmp_width > std::numeric_limits<std::uint16_t>::max() ||
49 tmp_height > std::numeric_limits<std::uint16_t>::max()) {
50 return false;
51 }
52 *width = static_cast<std::uint16_t>(tmp_width);
53 *height = static_cast<std::uint16_t>(tmp_height);
54 return true;
55 }
56
GetRational(const TiffDirectory::Tag & tag,const TiffDirectory & directory,const int data_size,PreviewImageData::Rational * data)57 bool GetRational(const TiffDirectory::Tag& tag, const TiffDirectory& directory,
58 const int data_size, PreviewImageData::Rational* data) {
59 std::vector<Rational> value;
60 if (directory.Get(tag, &value) &&
61 value.size() == static_cast<size_t>(data_size)) {
62 for (size_t i = 0; i < value.size(); ++i) {
63 data[i].numerator = value[i].numerator;
64 data[i].denominator = value[i].denominator;
65 }
66 return true;
67 }
68 return false;
69 }
70
FillGpsPreviewImageData(const TiffDirectory & gps_directory,PreviewImageData * preview_image_data)71 void FillGpsPreviewImageData(const TiffDirectory& gps_directory,
72 PreviewImageData* preview_image_data) {
73 if (gps_directory.Has(kGpsTagLatitudeRef) &&
74 gps_directory.Has(kGpsTagLatitude) &&
75 gps_directory.Has(kGpsTagLongitudeRef) &&
76 gps_directory.Has(kGpsTagLongitude) &&
77 gps_directory.Has(kGpsTagTimeStamp) &&
78 gps_directory.Has(kGpsTagDateStamp)) {
79 preview_image_data->gps.is_valid = false;
80 std::string value;
81 if (!gps_directory.Get(kGpsTagLatitudeRef, &value) || value.empty() ||
82 (value[0] != 'N' && value[0] != 'S') ||
83 !GetRational(kGpsTagLatitude, gps_directory, 3 /* data size */,
84 preview_image_data->gps.latitude)) {
85 return;
86 }
87 preview_image_data->gps.latitude_ref = value[0];
88
89 if (!gps_directory.Get(kGpsTagLongitudeRef, &value) || value.empty() ||
90 (value[0] != 'E' && value[0] != 'W') ||
91 !GetRational(kGpsTagLongitude, gps_directory, 3 /* data size */,
92 preview_image_data->gps.longitude)) {
93 return;
94 }
95 preview_image_data->gps.longitude_ref = value[0];
96
97 if (!GetRational(kGpsTagTimeStamp, gps_directory, 3 /* data size */,
98 preview_image_data->gps.time_stamp)) {
99 return;
100 }
101
102 const size_t kGpsDateStampSize = 11;
103 if (!gps_directory.Get(kGpsTagDateStamp,
104 &preview_image_data->gps.date_stamp)) {
105 return;
106 }
107 if (preview_image_data->gps.date_stamp.size() == kGpsDateStampSize) {
108 // Resize the date_stamp to remove the "NULL" at the end of string.
109 preview_image_data->gps.date_stamp.resize(kGpsDateStampSize - 1);
110 } else {
111 return;
112 }
113
114 if (gps_directory.Has(kGpsTagAltitudeRef) &&
115 gps_directory.Has(kGpsTagAltitude)) {
116 std::vector<std::uint8_t> bytes;
117 if (!gps_directory.Get(kGpsTagAltitudeRef, &bytes) || bytes.empty() ||
118 !GetRational(kGpsTagAltitude, gps_directory, 1,
119 &preview_image_data->gps.altitude)) {
120 return;
121 }
122 preview_image_data->gps.altitude_ref = bytes[0] != 0;
123 }
124 preview_image_data->gps.is_valid = true;
125 }
126 }
127
GetImageSize(const TiffDirectory & tiff_directory,StreamInterface * stream,Image * image)128 void GetImageSize(const TiffDirectory& tiff_directory, StreamInterface* stream,
129 Image* image) {
130 switch (image->format) {
131 case Image::kUncompressedRgb: {
132 GetFullDimension16(tiff_directory, &image->width, &image->height);
133 break;
134 }
135 case Image::kJpegCompressed: {
136 GetJpegDimensions(image->offset, stream, &image->width, &image->height);
137 break;
138 }
139 default: { return; }
140 }
141 }
142
FillPreviewImageData(const TiffDirectory & tiff_directory,StreamInterface * stream,PreviewImageData * preview_image_data)143 bool FillPreviewImageData(const TiffDirectory& tiff_directory,
144 StreamInterface* stream,
145 PreviewImageData* preview_image_data) {
146 bool success = true;
147 // Get preview or thumbnail. The code assumes that only thumbnails can be
148 // uncompressed. Preview images are always JPEG compressed.
149 Image image;
150 if (GetImageData(tiff_directory, stream, &image)) {
151 if (IsThumbnail(image)) {
152 preview_image_data->thumbnail = image;
153 } else if (image.format == Image::kJpegCompressed) {
154 preview_image_data->preview = image;
155 }
156 }
157
158 // Get exif_orientation if it was not set already.
159 if (tiff_directory.Has(kTiffTagOrientation) &&
160 preview_image_data->exif_orientation == 1) {
161 success &= tiff_directory.Get(kTiffTagOrientation,
162 &preview_image_data->exif_orientation);
163 }
164
165 // Get color_space
166 if (tiff_directory.Has(kExifTagColorSpace)) {
167 std::uint32_t color_space;
168 success &= tiff_directory.Get(kExifTagColorSpace, &color_space);
169 if (color_space == 1) {
170 preview_image_data->color_space = PreviewImageData::kSrgb;
171 } else if (color_space == 65535 || color_space == 2) {
172 preview_image_data->color_space = PreviewImageData::kAdobeRgb;
173 }
174 }
175
176 success &= GetFullDimension32(tiff_directory, &preview_image_data->full_width,
177 &preview_image_data->full_height);
178
179 if (tiff_directory.Has(kTiffTagMake)) {
180 success &= tiff_directory.Get(kTiffTagMake, &preview_image_data->maker);
181 }
182
183 if (tiff_directory.Has(kTiffTagModel)) {
184 success &= tiff_directory.Get(kTiffTagModel, &preview_image_data->model);
185 }
186
187 if (tiff_directory.Has(kTiffTagCfaPatternDim)) {
188 std::vector<std::uint32_t> cfa_pattern_dim;
189 if (tiff_directory.Get(kTiffTagCfaPatternDim, &cfa_pattern_dim) &&
190 cfa_pattern_dim.size() == 2) {
191 preview_image_data->cfa_pattern_dim[0] = cfa_pattern_dim[0];
192 preview_image_data->cfa_pattern_dim[1] = cfa_pattern_dim[1];
193 }
194 }
195
196 if (tiff_directory.Has(kExifTagDateTimeOriginal)) {
197 success &= tiff_directory.Get(kExifTagDateTimeOriginal,
198 &preview_image_data->date_time);
199 }
200
201 if (tiff_directory.Has(kExifTagIsoSpeed)) {
202 success &= tiff_directory.Get(kExifTagIsoSpeed, &preview_image_data->iso);
203 } else if (tiff_directory.Has(kPanaTagIso)) {
204 success &= tiff_directory.Get(kPanaTagIso, &preview_image_data->iso);
205 }
206
207 if (tiff_directory.Has(kExifTagExposureTime)) {
208 success &= GetRational(kExifTagExposureTime, tiff_directory, 1,
209 &preview_image_data->exposure_time);
210 }
211
212 if (tiff_directory.Has(kExifTagFnumber)) {
213 success &= GetRational(kExifTagFnumber, tiff_directory, 1,
214 &preview_image_data->fnumber);
215 }
216
217 if (tiff_directory.Has(kExifTagFocalLength)) {
218 success &= GetRational(kExifTagFocalLength, tiff_directory, 1,
219 &preview_image_data->focal_length);
220 }
221
222 return success;
223 }
224
FindFirstTagInIfds(const TiffDirectory::Tag & tag,const IfdVector & tiff_directory)225 const TiffDirectory* FindFirstTagInIfds(const TiffDirectory::Tag& tag,
226 const IfdVector& tiff_directory) {
227 for (std::uint32_t i = 0; i < tiff_directory.size(); ++i) {
228 if (tiff_directory[i].Has(tag)) {
229 return &tiff_directory[i];
230 }
231
232 // Recursively search sub directories.
233 const TiffDirectory* sub_directory =
234 FindFirstTagInIfds(tag, tiff_directory[i].GetSubDirectories());
235 if (sub_directory != NULL) {
236 return sub_directory;
237 }
238 }
239 return NULL;
240 }
241
242 // Return true if all data blocks are ordered one after the other without gaps.
OffsetsAreConsecutive(const std::vector<std::uint32_t> & strip_offsets,const std::vector<std::uint32_t> & strip_byte_counts)243 bool OffsetsAreConsecutive(
244 const std::vector<std::uint32_t>& strip_offsets,
245 const std::vector<std::uint32_t>& strip_byte_counts) {
246 if (strip_offsets.size() != strip_byte_counts.size() ||
247 strip_offsets.empty()) {
248 return false;
249 }
250
251 for (size_t i = 0; i < strip_offsets.size() - 1; ++i) {
252 if (strip_offsets[i] + strip_byte_counts[i] != strip_offsets[i + 1]) {
253 return false;
254 }
255 }
256 return true;
257 }
258
259 // Gets the SubIfd content.
ParseSubIfds(const std::uint32_t tiff_offset,const TagSet & desired_tags,const std::uint32_t max_number_ifds,const Endian endian,StreamInterface * stream,TiffDirectory * tiff_ifd)260 bool ParseSubIfds(const std::uint32_t tiff_offset, const TagSet& desired_tags,
261 const std::uint32_t max_number_ifds, const Endian endian,
262 StreamInterface* stream, TiffDirectory* tiff_ifd) {
263 if (tiff_ifd->Has(kTiffTagSubIfd)) {
264 std::uint32_t offset = 0;
265 std::uint32_t length = 0;
266 tiff_ifd->GetOffsetAndLength(kTiffTagSubIfd, TIFF_TYPE_LONG, &offset,
267 &length);
268 length /= 4; // length in bytes divided by 4 gives number of IFDs.
269 for (std::uint32_t j = 0; j < length && j < max_number_ifds; ++j) {
270 std::uint32_t sub_offset;
271 if (!Get32u(stream, offset + 4 * j, endian, &sub_offset)) {
272 return false;
273 }
274
275 std::uint32_t next_ifd_offset;
276 TiffDirectory sub_ifd(static_cast<Endian>(endian));
277 if (!ParseDirectory(tiff_offset, sub_offset, endian, desired_tags, stream,
278 &sub_ifd, &next_ifd_offset)) {
279 return false;
280 }
281
282 tiff_ifd->AddSubDirectory(sub_ifd);
283 }
284 }
285 return true;
286 }
287
288 } // namespace
289
Get16u(StreamInterface * stream,const std::uint32_t offset,const Endian & endian,std::uint16_t * value)290 bool Get16u(StreamInterface* stream, const std::uint32_t offset,
291 const Endian& endian, std::uint16_t* value) {
292 std::uint8_t data[2];
293 if (stream->GetData(offset, 2, data) == kOk) {
294 if (endian == kBigEndian) {
295 *value = (data[0] * 0x100) | data[1];
296 } else {
297 *value = (data[1] * 0x100) | data[0];
298 }
299 return true;
300 } else {
301 return false;
302 }
303 }
304
Get32u(StreamInterface * stream,const std::uint32_t offset,const Endian & endian,std::uint32_t * value)305 bool Get32u(StreamInterface* stream, const std::uint32_t offset,
306 const Endian& endian, std::uint32_t* value) {
307 std::uint8_t data[4];
308 if (stream->GetData(offset, 4, data) == kOk) {
309 if (endian == kBigEndian) {
310 *value = (data[0] * 0x1000000u) | (data[1] * 0x10000u) |
311 (data[2] * 0x100u) | data[3];
312 } else {
313 *value = (data[3] * 0x1000000u) | (data[2] * 0x10000u) |
314 (data[1] * 0x100u) | data[0];
315 }
316 return true;
317 } else {
318 return false;
319 }
320 }
321
GetData(const size_t offset,const size_t length,StreamInterface * stream,Error * error)322 std::vector<std::uint8_t> GetData(const size_t offset, const size_t length,
323 StreamInterface* stream, Error* error) {
324 // Read in chunks with a maximum size of 1 MiB.
325 const size_t kChunkSize = 1048576;
326
327 std::vector<std::uint8_t> data;
328 size_t processed_data = 0;
329 while (*error == kOk && processed_data < length) {
330 size_t chunk_length = kChunkSize;
331 if (length - data.size() < kChunkSize) {
332 chunk_length = length - data.size();
333 }
334
335 data.resize(processed_data + chunk_length);
336 *error = stream->GetData(offset + processed_data, chunk_length,
337 &data[processed_data]);
338
339 processed_data += chunk_length;
340 }
341 return data;
342 }
343
GetEndianness(const std::uint32_t tiff_offset,StreamInterface * stream,Endian * endian)344 bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream,
345 Endian* endian) {
346 const std::uint8_t kTiffBigEndianMagic[] = {'M', 'M'};
347 const std::uint8_t kTiffLittleEndianMagic[] = {'I', 'I'};
348 std::uint8_t tiff_endian[sizeof(kTiffBigEndianMagic)];
349 if (stream->GetData(tiff_offset, sizeof(tiff_endian), &tiff_endian[0]) !=
350 kOk) {
351 return false;
352 }
353
354 if (!memcmp(tiff_endian, kTiffLittleEndianMagic, sizeof(tiff_endian))) {
355 *endian = kLittleEndian;
356 return true;
357 } else if (!memcmp(tiff_endian, kTiffBigEndianMagic, sizeof(tiff_endian))) {
358 *endian = kBigEndian;
359 return true;
360 } else {
361 return false;
362 }
363 }
364
GetImageData(const TiffDirectory & tiff_directory,StreamInterface * stream,Image * image)365 bool GetImageData(const TiffDirectory& tiff_directory, StreamInterface* stream,
366 Image* image) {
367 std::uint32_t length = 0;
368 std::uint32_t offset = 0;
369
370 if (tiff_directory.Has(kTiffTagJpegOffset) &&
371 tiff_directory.Has(kTiffTagJpegByteCount)) {
372 if (!tiff_directory.Get(kTiffTagJpegOffset, &offset) ||
373 !tiff_directory.Get(kTiffTagJpegByteCount, &length)) {
374 return false;
375 }
376 image->format = Image::kJpegCompressed;
377 } else if (tiff_directory.Has(kTiffTagStripOffsets) &&
378 tiff_directory.Has(kTiffTagStripByteCounts)) {
379 std::vector<std::uint32_t> strip_offsets;
380 std::vector<std::uint32_t> strip_byte_counts;
381 if (!tiff_directory.Get(kTiffTagStripOffsets, &strip_offsets) ||
382 !tiff_directory.Get(kTiffTagStripByteCounts, &strip_byte_counts)) {
383 return false;
384 }
385
386 std::uint32_t compression = 0;
387 if (!OffsetsAreConsecutive(strip_offsets, strip_byte_counts) ||
388 !tiff_directory.Get(kTiffTagCompression, &compression)) {
389 return false;
390 }
391
392 std::uint32_t photometric_interpretation = 0;
393 if (tiff_directory.Get(kTiffTagPhotometric, &photometric_interpretation) &&
394 photometric_interpretation != 2 /* RGB */ &&
395 photometric_interpretation != 6 /* YCbCr */) {
396 return false;
397 }
398
399 switch (compression) {
400 case 1: /*uncompressed*/
401 image->format = Image::kUncompressedRgb;
402 break;
403 case 6: /* JPEG(old) */
404 case 7: /* JPEG */
405 image->format = Image::kJpegCompressed;
406 break;
407 default:
408 return false;
409 }
410 length = static_cast<std::uint32_t>(
411 std::accumulate(strip_byte_counts.begin(), strip_byte_counts.end(), 0));
412 offset = strip_offsets[0];
413 } else if (tiff_directory.Has(kPanaTagJpegImage)) {
414 if (!tiff_directory.GetOffsetAndLength(
415 kPanaTagJpegImage, TIFF_TYPE_UNDEFINED, &offset, &length)) {
416 return false;
417 }
418 image->format = Image::kJpegCompressed;
419 } else {
420 return false;
421 }
422
423 image->length = length;
424 image->offset = offset;
425 GetImageSize(tiff_directory, stream, image);
426 return true;
427 }
428
GetJpegDimensions(const std::uint32_t jpeg_offset,StreamInterface * stream,std::uint16_t * width,std::uint16_t * height)429 bool GetJpegDimensions(const std::uint32_t jpeg_offset, StreamInterface* stream,
430 std::uint16_t* width, std::uint16_t* height) {
431 const Endian endian = kBigEndian;
432 std::uint32_t offset = jpeg_offset;
433 std::uint16_t segment;
434
435 // Parse the JPEG header until we find Frame0 which contains the image width
436 // and height or the actual image data starts (StartOfScan)
437 do {
438 if (!Get16u(stream, offset, endian, &segment)) {
439 return false;
440 }
441 offset += 2;
442
443 switch (segment) {
444 case kStartOfImage:
445 break;
446 case kStartOfFrame:
447 return Get16u(stream, offset + 3, endian, height) &&
448 Get16u(stream, offset + 5, endian, width);
449 default: {
450 std::uint16_t length;
451 if (!Get16u(stream, offset, endian, &length)) {
452 return false;
453 }
454 offset += length;
455 }
456 }
457 } while (segment != kStartOfScan);
458
459 // No width and hight information found.
460 return false;
461 }
462
IsThumbnail(const Image & image,const int max_dimension)463 bool IsThumbnail(const Image& image, const int max_dimension) {
464 return image.width <= max_dimension && image.height <= max_dimension;
465 }
466
ParseDirectory(const std::uint32_t tiff_offset,const std::uint32_t ifd_offset,const Endian endian,const TagSet & desired_tags,StreamInterface * stream,TiffDirectory * tiff_directory,std::uint32_t * next_ifd_offset)467 bool ParseDirectory(const std::uint32_t tiff_offset,
468 const std::uint32_t ifd_offset, const Endian endian,
469 const TagSet& desired_tags, StreamInterface* stream,
470 TiffDirectory* tiff_directory,
471 std::uint32_t* next_ifd_offset) {
472 std::uint16_t number_of_entries;
473 if (!Get16u(stream, ifd_offset, endian, &number_of_entries)) {
474 return false;
475 }
476
477 for (std::uint32_t i = 0;
478 i < static_cast<std::uint32_t>(number_of_entries) * 12; i += 12) {
479 std::uint16_t tag;
480 std::uint16_t type;
481 std::uint32_t number_of_elements;
482 if (Get16u(stream, ifd_offset + 2 + i, endian, &tag) &&
483 Get16u(stream, ifd_offset + 4 + i, endian, &type) &&
484 Get32u(stream, ifd_offset + 6 + i, endian, &number_of_elements)) {
485 // Check if the current tag should be handled.
486 if (desired_tags.count(static_cast<TiffDirectory::Tag>(tag)) != 1) {
487 continue;
488 }
489 } else {
490 return false;
491 }
492
493 const size_t type_size = SizeOfType(type, nullptr /* no error */);
494
495 // Check that type_size * number_of_elements does not exceed UINT32_MAX.
496 if (type_size != 0 && number_of_elements > UINT32_MAX / type_size) {
497 return false;
498 }
499 const size_t byte_count =
500 type_size * static_cast<size_t>(number_of_elements);
501
502 std::uint32_t value_offset;
503 if (byte_count > 4 &&
504 Get32u(stream, ifd_offset + 10 + i, endian, &value_offset)) {
505 value_offset += tiff_offset;
506 } else if (byte_count != 0) {
507 value_offset = ifd_offset + 10 + i;
508 } else {
509 // Ignore entries with an invalid byte count.
510 continue;
511 }
512
513 Error error = kOk;
514 const std::vector<std::uint8_t> data =
515 GetData(value_offset, byte_count, stream, &error);
516 if (error != kOk) {
517 return false;
518 }
519 tiff_directory->AddEntry(tag, type, number_of_elements, value_offset, data);
520 }
521
522 return Get32u(stream, ifd_offset + 2u + number_of_entries * 12u, endian,
523 next_ifd_offset);
524 }
525
GetExifOrientation(StreamInterface * stream,const std::uint32_t offset,std::uint32_t * orientation)526 bool GetExifOrientation(StreamInterface* stream, const std::uint32_t offset,
527 std::uint32_t* orientation) {
528 const TagSet kOrientationTagSet = {kTiffTagOrientation};
529 const std::uint32_t kNumberOfIfds = 1;
530
531 TiffContent tiff_content;
532 if (!TiffParser(stream, offset)
533 .Parse(kOrientationTagSet, kNumberOfIfds, &tiff_content)) {
534 return false;
535 }
536
537 for (const auto& tiff_directory : tiff_content.tiff_directory) {
538 if (tiff_directory.Has(kTiffTagOrientation) &&
539 tiff_directory.Get(kTiffTagOrientation, orientation)) {
540 return true;
541 }
542 }
543
544 return false;
545 }
546
GetFullDimension32(const TiffDirectory & tiff_directory,std::uint32_t * width,std::uint32_t * height)547 bool GetFullDimension32(const TiffDirectory& tiff_directory,
548 std::uint32_t* width, std::uint32_t* height) {
549 // The sub file type needs to be 0 (main image) to contain a valid full
550 // dimensions. This is important in particular for DNG.
551 if (tiff_directory.Has(kTiffTagSubFileType)) {
552 std::uint32_t sub_file_type;
553 if (!tiff_directory.Get(kTiffTagSubFileType, &sub_file_type) ||
554 sub_file_type != 0) {
555 return false;
556 }
557 }
558
559 if (tiff_directory.Has(kExifTagDefaultCropSize)) {
560 std::vector<std::uint32_t> crop(2);
561 std::vector<Rational> crop_rational(2);
562 if (tiff_directory.Get(kExifTagDefaultCropSize, &crop)) {
563 *width = crop[0];
564 *height = crop[1];
565 } else if (tiff_directory.Get(kExifTagDefaultCropSize, &crop_rational) &&
566 crop_rational[0].denominator != 0 &&
567 crop_rational[1].denominator != 0) {
568 *width = crop_rational[0].numerator / crop_rational[0].denominator;
569 *height = crop_rational[1].numerator / crop_rational[1].denominator;
570 } else {
571 return false;
572 }
573 } else if (tiff_directory.Has(kExifTagWidth) &&
574 tiff_directory.Has(kExifTagHeight)) {
575 if (!tiff_directory.Get(kExifTagWidth, width) ||
576 !tiff_directory.Get(kExifTagHeight, height)) {
577 return false;
578 }
579 } else if (tiff_directory.Has(kTiffTagImageWidth) &&
580 tiff_directory.Has(kTiffTagImageLength)) {
581 if (!tiff_directory.Get(kTiffTagImageWidth, width) ||
582 !tiff_directory.Get(kTiffTagImageLength, height)) {
583 return false;
584 }
585 } else if (tiff_directory.Has(kPanaTagTopBorder) &&
586 tiff_directory.Has(kPanaTagLeftBorder) &&
587 tiff_directory.Has(kPanaTagBottomBorder) &&
588 tiff_directory.Has(kPanaTagRightBorder)) {
589 std::uint32_t left;
590 std::uint32_t right;
591 std::uint32_t top;
592 std::uint32_t bottom;
593 if (tiff_directory.Get(kPanaTagLeftBorder, &left) &&
594 tiff_directory.Get(kPanaTagRightBorder, &right) &&
595 tiff_directory.Get(kPanaTagTopBorder, &top) &&
596 tiff_directory.Get(kPanaTagBottomBorder, &bottom) && bottom > top &&
597 right > left) {
598 *height = bottom - top;
599 *width = right - left;
600 } else {
601 return false;
602 }
603 }
604 return true;
605 }
606
TiffParser(StreamInterface * stream)607 TiffParser::TiffParser(StreamInterface* stream) : stream_(stream) {}
608
TiffParser(StreamInterface * stream,const std::uint32_t offset)609 TiffParser::TiffParser(StreamInterface* stream, const std::uint32_t offset)
610 : stream_(stream), tiff_offset_(offset) {}
611
GetPreviewImageData(const TiffContent & tiff_content,PreviewImageData * preview_image_data)612 bool TiffParser::GetPreviewImageData(const TiffContent& tiff_content,
613 PreviewImageData* preview_image_data) {
614 bool success = true;
615 for (const auto& tiff_directory : tiff_content.tiff_directory) {
616 success = FillPreviewImageData(tiff_directory, stream_, preview_image_data);
617 if (success && tiff_directory.Has(kTiffTagExifIfd) &&
618 tiff_content.exif_directory) {
619 success = FillPreviewImageData(*tiff_content.exif_directory, stream_,
620 preview_image_data);
621 }
622 if (success && tiff_directory.Has(kExifTagGps) &&
623 tiff_content.gps_directory) {
624 FillGpsPreviewImageData(*tiff_content.gps_directory, preview_image_data);
625 }
626 for (const auto& sub_directory : tiff_directory.GetSubDirectories()) {
627 if (success) {
628 success =
629 FillPreviewImageData(sub_directory, stream_, preview_image_data);
630 }
631 }
632 }
633 return success;
634 }
635
Parse(const TagSet & desired_tags,const std::uint16_t max_number_ifds,TiffContent * tiff_content)636 bool TiffParser::Parse(const TagSet& desired_tags,
637 const std::uint16_t max_number_ifds,
638 TiffContent* tiff_content) {
639 if (!tiff_content->tiff_directory.empty()) {
640 return false; // You shall call Parse() only once.
641 }
642
643 const std::uint32_t kTiffIdentifierSize = 4;
644 std::uint32_t offset_to_ifd = 0;
645 if (!GetEndianness(tiff_offset_, stream_, &endian_) ||
646 !Get32u(stream_, tiff_offset_ + kTiffIdentifierSize, endian_,
647 &offset_to_ifd)) {
648 return false;
649 }
650
651 if (!ParseIfd(tiff_offset_ + offset_to_ifd, desired_tags, max_number_ifds,
652 &tiff_content->tiff_directory)) {
653 return false;
654 }
655
656 // Get the Exif data.
657 if (FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory) !=
658 nullptr) {
659 const TiffDirectory* tiff_ifd =
660 FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory);
661 std::uint32_t offset;
662 if (tiff_ifd->Get(kTiffTagExifIfd, &offset)) {
663 tiff_content->exif_directory.reset(new TiffDirectory(endian_));
664 std::uint32_t next_ifd_offset;
665 if (!ParseDirectory(
666 tiff_offset_, tiff_offset_ + offset, endian_, desired_tags,
667 stream_, tiff_content->exif_directory.get(), &next_ifd_offset)) {
668 return false;
669 }
670
671 return ParseGpsData(tiff_ifd, tiff_content);
672 }
673 }
674
675 // Get the GPS data from the tiff ifd.
676 if (FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory) !=
677 nullptr) {
678 const TiffDirectory* tiff_ifd =
679 FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory);
680 return ParseGpsData(tiff_ifd, tiff_content);
681 }
682
683 return true;
684 }
685
ParseIfd(const std::uint32_t offset_to_ifd,const TagSet & desired_tags,const std::uint16_t max_number_ifds,IfdVector * tiff_directory)686 bool TiffParser::ParseIfd(const std::uint32_t offset_to_ifd,
687 const TagSet& desired_tags,
688 const std::uint16_t max_number_ifds,
689 IfdVector* tiff_directory) {
690 std::uint32_t next_ifd_offset;
691 TiffDirectory tiff_ifd(static_cast<Endian>(endian_));
692 if (!ParseDirectory(tiff_offset_, offset_to_ifd, endian_, desired_tags,
693 stream_, &tiff_ifd, &next_ifd_offset) ||
694 !ParseSubIfds(tiff_offset_, desired_tags, max_number_ifds, endian_,
695 stream_, &tiff_ifd)) {
696 return false;
697 }
698
699 tiff_directory->push_back(tiff_ifd);
700 if (next_ifd_offset != 0 && tiff_directory->size() < max_number_ifds) {
701 return ParseIfd(tiff_offset_ + next_ifd_offset, desired_tags,
702 max_number_ifds, tiff_directory);
703 }
704 return true;
705 }
706
ParseGpsData(const TiffDirectory * tiff_ifd,TiffContent * tiff_content)707 bool TiffParser::ParseGpsData(const TiffDirectory* tiff_ifd,
708 TiffContent* tiff_content) {
709 std::uint32_t offset;
710 if (tiff_ifd->Get(kExifTagGps, &offset)) {
711 tiff_content->gps_directory.reset(new TiffDirectory(endian_));
712 const TagSet gps_tags = {kGpsTagLatitudeRef, kGpsTagLatitude,
713 kGpsTagLongitudeRef, kGpsTagLongitude,
714 kGpsTagAltitudeRef, kGpsTagAltitude,
715 kGpsTagTimeStamp, kGpsTagDateStamp};
716 std::uint32_t next_ifd_offset;
717 return ParseDirectory(tiff_offset_, tiff_offset_ + offset, endian_,
718 gps_tags, stream_, tiff_content->gps_directory.get(),
719 &next_ifd_offset);
720 }
721 return true;
722 }
723
724 } // namespace piex
725