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 25namespace piex { 26namespace { 27 28using tiff_directory::Endian; 29using tiff_directory::Rational; 30using tiff_directory::SRational; 31using tiff_directory::SizeOfType; 32using tiff_directory::TIFF_TYPE_LONG; 33using tiff_directory::TIFF_TYPE_UNDEFINED; 34using tiff_directory::TiffDirectory; 35using tiff_directory::kBigEndian; 36using tiff_directory::kLittleEndian; 37 38// Specifies all tags that might be of interest to parse JPEG data. 39const std::uint32_t kStartOfFrame = 0xFFC0; 40const std::uint32_t kStartOfImage = 0xFFD8; 41const std::uint32_t kStartOfScan = 0xFFDA; 42 43bool 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 57bool 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 71void 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 128void 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 143bool 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 225const 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. 243bool 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. 260bool 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 290bool 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 305bool 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 322std::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 344bool 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 365bool 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 429bool 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 463bool IsThumbnail(const Image& image, const int max_dimension) { 464 return image.width <= max_dimension && image.height <= max_dimension; 465} 466 467bool 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 526bool 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 547bool 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 if (!GetFullCropDimension(tiff_directory, width, height)) { 561 return false; 562 } 563 } else if (tiff_directory.Has(kExifTagWidth) && 564 tiff_directory.Has(kExifTagHeight)) { 565 if (!tiff_directory.Get(kExifTagWidth, width) || 566 !tiff_directory.Get(kExifTagHeight, height)) { 567 return false; 568 } 569 } else if (tiff_directory.Has(kTiffTagImageWidth) && 570 tiff_directory.Has(kTiffTagImageLength)) { 571 if (!tiff_directory.Get(kTiffTagImageWidth, width) || 572 !tiff_directory.Get(kTiffTagImageLength, height)) { 573 return false; 574 } 575 } else if (tiff_directory.Has(kPanaTagTopBorder) && 576 tiff_directory.Has(kPanaTagLeftBorder) && 577 tiff_directory.Has(kPanaTagBottomBorder) && 578 tiff_directory.Has(kPanaTagRightBorder)) { 579 std::uint32_t left; 580 std::uint32_t right; 581 std::uint32_t top; 582 std::uint32_t bottom; 583 if (tiff_directory.Get(kPanaTagLeftBorder, &left) && 584 tiff_directory.Get(kPanaTagRightBorder, &right) && 585 tiff_directory.Get(kPanaTagTopBorder, &top) && 586 tiff_directory.Get(kPanaTagBottomBorder, &bottom) && bottom > top && 587 right > left) { 588 *height = bottom - top; 589 *width = right - left; 590 } else { 591 return false; 592 } 593 } 594 return true; 595} 596 597bool GetFullCropDimension(const tiff_directory::TiffDirectory& tiff_directory, 598 std::uint32_t* width, std::uint32_t* height) { 599 if (tiff_directory.Has(kExifTagDefaultCropSize)) { 600 std::vector<std::uint32_t> crop(2); 601 std::vector<Rational> crop_rational(2); 602 if (tiff_directory.Get(kExifTagDefaultCropSize, &crop)) { 603 *width = crop[0]; 604 *height = crop[1]; 605 } else if (tiff_directory.Get(kExifTagDefaultCropSize, &crop_rational) && 606 crop_rational[0].denominator != 0 && 607 crop_rational[1].denominator != 0) { 608 *width = crop_rational[0].numerator / crop_rational[0].denominator; 609 *height = crop_rational[1].numerator / crop_rational[1].denominator; 610 } else { 611 return false; 612 } 613 } 614 615 return true; 616} 617 618TiffParser::TiffParser(StreamInterface* stream) : stream_(stream) {} 619 620TiffParser::TiffParser(StreamInterface* stream, const std::uint32_t offset) 621 : stream_(stream), tiff_offset_(offset) {} 622 623bool TiffParser::GetPreviewImageData(const TiffContent& tiff_content, 624 PreviewImageData* preview_image_data) { 625 bool success = true; 626 for (const auto& tiff_directory : tiff_content.tiff_directory) { 627 success = FillPreviewImageData(tiff_directory, stream_, preview_image_data); 628 if (success && tiff_directory.Has(kTiffTagExifIfd) && 629 tiff_content.exif_directory) { 630 success = FillPreviewImageData(*tiff_content.exif_directory, stream_, 631 preview_image_data); 632 } 633 if (success && tiff_directory.Has(kExifTagGps) && 634 tiff_content.gps_directory) { 635 FillGpsPreviewImageData(*tiff_content.gps_directory, preview_image_data); 636 } 637 for (const auto& sub_directory : tiff_directory.GetSubDirectories()) { 638 if (success) { 639 success = 640 FillPreviewImageData(sub_directory, stream_, preview_image_data); 641 } 642 } 643 } 644 return success; 645} 646 647bool TiffParser::Parse(const TagSet& desired_tags, 648 const std::uint16_t max_number_ifds, 649 TiffContent* tiff_content) { 650 if (!tiff_content->tiff_directory.empty()) { 651 return false; // You shall call Parse() only once. 652 } 653 654 const std::uint32_t kTiffIdentifierSize = 4; 655 std::uint32_t offset_to_ifd = 0; 656 if (!GetEndianness(tiff_offset_, stream_, &endian_) || 657 !Get32u(stream_, tiff_offset_ + kTiffIdentifierSize, endian_, 658 &offset_to_ifd)) { 659 return false; 660 } 661 662 if (!ParseIfd(tiff_offset_ + offset_to_ifd, desired_tags, max_number_ifds, 663 &tiff_content->tiff_directory)) { 664 return false; 665 } 666 667 // Get the Exif data. 668 if (FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory) != 669 nullptr) { 670 const TiffDirectory* tiff_ifd = 671 FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory); 672 std::uint32_t offset; 673 if (tiff_ifd->Get(kTiffTagExifIfd, &offset)) { 674 tiff_content->exif_directory.reset(new TiffDirectory(endian_)); 675 std::uint32_t next_ifd_offset; 676 if (!ParseDirectory( 677 tiff_offset_, tiff_offset_ + offset, endian_, desired_tags, 678 stream_, tiff_content->exif_directory.get(), &next_ifd_offset)) { 679 return false; 680 } 681 682 return ParseGpsData(tiff_ifd, tiff_content); 683 } 684 } 685 686 // Get the GPS data from the tiff ifd. 687 if (FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory) != 688 nullptr) { 689 const TiffDirectory* tiff_ifd = 690 FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory); 691 return ParseGpsData(tiff_ifd, tiff_content); 692 } 693 694 return true; 695} 696 697bool TiffParser::ParseIfd(const std::uint32_t offset_to_ifd, 698 const TagSet& desired_tags, 699 const std::uint16_t max_number_ifds, 700 IfdVector* tiff_directory) { 701 std::uint32_t next_ifd_offset; 702 TiffDirectory tiff_ifd(static_cast<Endian>(endian_)); 703 if (!ParseDirectory(tiff_offset_, offset_to_ifd, endian_, desired_tags, 704 stream_, &tiff_ifd, &next_ifd_offset) || 705 !ParseSubIfds(tiff_offset_, desired_tags, max_number_ifds, endian_, 706 stream_, &tiff_ifd)) { 707 return false; 708 } 709 710 tiff_directory->push_back(tiff_ifd); 711 if (next_ifd_offset != 0 && tiff_directory->size() < max_number_ifds) { 712 return ParseIfd(tiff_offset_ + next_ifd_offset, desired_tags, 713 max_number_ifds, tiff_directory); 714 } 715 return true; 716} 717 718bool TiffParser::ParseGpsData(const TiffDirectory* tiff_ifd, 719 TiffContent* tiff_content) { 720 std::uint32_t offset; 721 if (tiff_ifd->Get(kExifTagGps, &offset)) { 722 tiff_content->gps_directory.reset(new TiffDirectory(endian_)); 723 const TagSet gps_tags = {kGpsTagLatitudeRef, kGpsTagLatitude, 724 kGpsTagLongitudeRef, kGpsTagLongitude, 725 kGpsTagAltitudeRef, kGpsTagAltitude, 726 kGpsTagTimeStamp, kGpsTagDateStamp}; 727 std::uint32_t next_ifd_offset; 728 return ParseDirectory(tiff_offset_, tiff_offset_ + offset, endian_, 729 gps_tags, stream_, tiff_content->gps_directory.get(), 730 &next_ifd_offset); 731 } 732 return true; 733} 734 735} // namespace piex 736