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 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 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 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 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 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 if (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 } else { 175 success = false; 176 } 177 } 178 179 success &= GetFullDimension32(tiff_directory, &preview_image_data->full_width, 180 &preview_image_data->full_height); 181 182 if (tiff_directory.Has(kTiffTagMake)) { 183 success &= tiff_directory.Get(kTiffTagMake, &preview_image_data->maker); 184 } 185 186 if (tiff_directory.Has(kTiffTagModel)) { 187 success &= tiff_directory.Get(kTiffTagModel, &preview_image_data->model); 188 } 189 190 if (tiff_directory.Has(kTiffTagCfaPatternDim)) { 191 std::vector<std::uint32_t> cfa_pattern_dim; 192 if (tiff_directory.Get(kTiffTagCfaPatternDim, &cfa_pattern_dim) && 193 cfa_pattern_dim.size() == 2) { 194 preview_image_data->cfa_pattern_dim[0] = cfa_pattern_dim[0]; 195 preview_image_data->cfa_pattern_dim[1] = cfa_pattern_dim[1]; 196 } 197 } 198 199 if (tiff_directory.Has(kExifTagDateTimeOriginal)) { 200 success &= tiff_directory.Get(kExifTagDateTimeOriginal, 201 &preview_image_data->date_time); 202 } 203 204 if (tiff_directory.Has(kExifTagIsoSpeed)) { 205 success &= tiff_directory.Get(kExifTagIsoSpeed, &preview_image_data->iso); 206 } else if (tiff_directory.Has(kPanaTagIso)) { 207 success &= tiff_directory.Get(kPanaTagIso, &preview_image_data->iso); 208 } 209 210 if (tiff_directory.Has(kExifTagExposureTime)) { 211 success &= GetRational(kExifTagExposureTime, tiff_directory, 1, 212 &preview_image_data->exposure_time); 213 } 214 215 if (tiff_directory.Has(kExifTagFnumber)) { 216 success &= GetRational(kExifTagFnumber, tiff_directory, 1, 217 &preview_image_data->fnumber); 218 } 219 220 if (tiff_directory.Has(kExifTagFocalLength)) { 221 success &= GetRational(kExifTagFocalLength, tiff_directory, 1, 222 &preview_image_data->focal_length); 223 } 224 225 return success; 226 } 227 228 const TiffDirectory* FindFirstTagInIfds(const TiffDirectory::Tag& tag, 229 const IfdVector& tiff_directory) { 230 for (std::uint32_t i = 0; i < tiff_directory.size(); ++i) { 231 if (tiff_directory[i].Has(tag)) { 232 return &tiff_directory[i]; 233 } 234 235 // Recursively search sub directories. 236 const TiffDirectory* sub_directory = 237 FindFirstTagInIfds(tag, tiff_directory[i].GetSubDirectories()); 238 if (sub_directory != NULL) { 239 return sub_directory; 240 } 241 } 242 return NULL; 243 } 244 245 // Return true if all data blocks are ordered one after the other without gaps. 246 bool OffsetsAreConsecutive( 247 const std::vector<std::uint32_t>& strip_offsets, 248 const std::vector<std::uint32_t>& strip_byte_counts) { 249 if (strip_offsets.size() != strip_byte_counts.size() || 250 strip_offsets.empty()) { 251 return false; 252 } 253 254 for (size_t i = 0; i < strip_offsets.size() - 1; ++i) { 255 if (strip_offsets[i] + strip_byte_counts[i] != strip_offsets[i + 1]) { 256 return false; 257 } 258 } 259 return true; 260 } 261 262 // Gets the SubIfd content. 263 bool ParseSubIfds(const std::uint32_t tiff_offset, const TagSet& desired_tags, 264 const std::uint32_t max_number_ifds, const Endian endian, 265 StreamInterface* stream, TiffDirectory* tiff_ifd) { 266 if (tiff_ifd->Has(kTiffTagSubIfd)) { 267 std::uint32_t offset = 0; 268 std::uint32_t length = 0; 269 tiff_ifd->GetOffsetAndLength(kTiffTagSubIfd, TIFF_TYPE_LONG, &offset, 270 &length); 271 length /= 4; // length in bytes divided by 4 gives number of IFDs. 272 for (std::uint32_t j = 0; j < length && j < max_number_ifds; ++j) { 273 std::uint32_t sub_offset; 274 if (!Get32u(stream, offset + 4 * j, endian, &sub_offset)) { 275 return false; 276 } 277 278 std::uint32_t next_ifd_offset; 279 TiffDirectory sub_ifd(static_cast<Endian>(endian)); 280 if (!ParseDirectory(tiff_offset, sub_offset, endian, desired_tags, stream, 281 &sub_ifd, &next_ifd_offset)) { 282 return false; 283 } 284 285 tiff_ifd->AddSubDirectory(sub_ifd); 286 } 287 } 288 return true; 289 } 290 291 } // namespace 292 293 bool Get16u(StreamInterface* stream, const std::uint32_t offset, 294 const Endian& endian, std::uint16_t* value) { 295 std::uint8_t data[2]; 296 if (stream->GetData(offset, 2, data) == kOk) { 297 if (endian == kBigEndian) { 298 *value = (data[0] * 0x100) | data[1]; 299 } else { 300 *value = (data[1] * 0x100) | data[0]; 301 } 302 return true; 303 } else { 304 return false; 305 } 306 } 307 308 bool Get32u(StreamInterface* stream, const std::uint32_t offset, 309 const Endian& endian, std::uint32_t* value) { 310 std::uint8_t data[4]; 311 if (stream->GetData(offset, 4, data) == kOk) { 312 if (endian == kBigEndian) { 313 *value = (data[0] * 0x1000000u) | (data[1] * 0x10000u) | 314 (data[2] * 0x100u) | data[3]; 315 } else { 316 *value = (data[3] * 0x1000000u) | (data[2] * 0x10000u) | 317 (data[1] * 0x100u) | data[0]; 318 } 319 return true; 320 } else { 321 return false; 322 } 323 } 324 325 std::vector<std::uint8_t> GetData(const size_t offset, const size_t length, 326 StreamInterface* stream, Error* error) { 327 // Read in chunks with a maximum size of 1 MiB. 328 const size_t kChunkSize = 1048576; 329 330 std::vector<std::uint8_t> data; 331 size_t processed_data = 0; 332 while (*error == kOk && processed_data < length) { 333 size_t chunk_length = kChunkSize; 334 if (length - data.size() < kChunkSize) { 335 chunk_length = length - data.size(); 336 } 337 338 data.resize(processed_data + chunk_length); 339 *error = stream->GetData(offset + processed_data, chunk_length, 340 &data[processed_data]); 341 342 processed_data += chunk_length; 343 } 344 return data; 345 } 346 347 bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream, 348 Endian* endian) { 349 const std::uint8_t kTiffBigEndianMagic[] = {'M', 'M'}; 350 const std::uint8_t kTiffLittleEndianMagic[] = {'I', 'I'}; 351 std::uint8_t tiff_endian[sizeof(kTiffBigEndianMagic)]; 352 if (stream->GetData(tiff_offset, sizeof(tiff_endian), &tiff_endian[0]) != 353 kOk) { 354 return false; 355 } 356 357 if (!memcmp(tiff_endian, kTiffLittleEndianMagic, sizeof(tiff_endian))) { 358 *endian = kLittleEndian; 359 return true; 360 } else if (!memcmp(tiff_endian, kTiffBigEndianMagic, sizeof(tiff_endian))) { 361 *endian = kBigEndian; 362 return true; 363 } else { 364 return false; 365 } 366 } 367 368 bool GetImageData(const TiffDirectory& tiff_directory, StreamInterface* stream, 369 Image* image) { 370 std::uint32_t length = 0; 371 std::uint32_t offset = 0; 372 373 if (tiff_directory.Has(kTiffTagJpegOffset) && 374 tiff_directory.Has(kTiffTagJpegByteCount)) { 375 if (!tiff_directory.Get(kTiffTagJpegOffset, &offset) || 376 !tiff_directory.Get(kTiffTagJpegByteCount, &length)) { 377 return false; 378 } 379 image->format = Image::kJpegCompressed; 380 } else if (tiff_directory.Has(kTiffTagStripOffsets) && 381 tiff_directory.Has(kTiffTagStripByteCounts)) { 382 std::vector<std::uint32_t> strip_offsets; 383 std::vector<std::uint32_t> strip_byte_counts; 384 if (!tiff_directory.Get(kTiffTagStripOffsets, &strip_offsets) || 385 !tiff_directory.Get(kTiffTagStripByteCounts, &strip_byte_counts)) { 386 return false; 387 } 388 389 std::uint32_t compression = 0; 390 if (!OffsetsAreConsecutive(strip_offsets, strip_byte_counts) || 391 !tiff_directory.Get(kTiffTagCompression, &compression)) { 392 return false; 393 } 394 395 std::uint32_t photometric_interpretation = 0; 396 if (tiff_directory.Get(kTiffTagPhotometric, &photometric_interpretation) && 397 photometric_interpretation != 2 /* RGB */ && 398 photometric_interpretation != 6 /* YCbCr */) { 399 return false; 400 } 401 402 switch (compression) { 403 case 1: /*uncompressed*/ 404 image->format = Image::kUncompressedRgb; 405 break; 406 case 6: /* JPEG(old) */ 407 case 7: /* JPEG */ 408 image->format = Image::kJpegCompressed; 409 break; 410 default: 411 return false; 412 } 413 length = static_cast<std::uint32_t>( 414 std::accumulate(strip_byte_counts.begin(), strip_byte_counts.end(), 0)); 415 offset = strip_offsets[0]; 416 } else if (tiff_directory.Has(kPanaTagJpegImage)) { 417 if (!tiff_directory.GetOffsetAndLength( 418 kPanaTagJpegImage, TIFF_TYPE_UNDEFINED, &offset, &length)) { 419 return false; 420 } 421 image->format = Image::kJpegCompressed; 422 } else { 423 return false; 424 } 425 426 image->length = length; 427 image->offset = offset; 428 GetImageSize(tiff_directory, stream, image); 429 return true; 430 } 431 432 bool GetJpegDimensions(const std::uint32_t jpeg_offset, StreamInterface* stream, 433 std::uint16_t* width, std::uint16_t* height) { 434 const Endian endian = kBigEndian; 435 std::uint32_t offset = jpeg_offset; 436 std::uint16_t segment; 437 438 // Parse the JPEG header until we find Frame0 which contains the image width 439 // and height or the actual image data starts (StartOfScan) 440 do { 441 if (!Get16u(stream, offset, endian, &segment)) { 442 return false; 443 } 444 offset += 2; 445 446 switch (segment) { 447 case kStartOfImage: 448 break; 449 case kStartOfFrame: 450 return Get16u(stream, offset + 3, endian, height) && 451 Get16u(stream, offset + 5, endian, width); 452 default: { 453 std::uint16_t length; 454 if (!Get16u(stream, offset, endian, &length)) { 455 return false; 456 } 457 offset += length; 458 } 459 } 460 } while (segment != kStartOfScan); 461 462 // No width and hight information found. 463 return false; 464 } 465 466 bool IsThumbnail(const Image& image, const int max_dimension) { 467 return image.width <= max_dimension && image.height <= max_dimension; 468 } 469 470 bool ParseDirectory(const std::uint32_t tiff_offset, 471 const std::uint32_t ifd_offset, const Endian endian, 472 const TagSet& desired_tags, StreamInterface* stream, 473 TiffDirectory* tiff_directory, 474 std::uint32_t* next_ifd_offset) { 475 std::uint16_t number_of_entries; 476 if (!Get16u(stream, ifd_offset, endian, &number_of_entries)) { 477 return false; 478 } 479 480 for (std::uint32_t i = 0; 481 i < static_cast<std::uint32_t>(number_of_entries) * 12; i += 12) { 482 std::uint16_t tag; 483 std::uint16_t type; 484 std::uint32_t number_of_elements; 485 if (Get16u(stream, ifd_offset + 2 + i, endian, &tag) && 486 Get16u(stream, ifd_offset + 4 + i, endian, &type) && 487 Get32u(stream, ifd_offset + 6 + i, endian, &number_of_elements)) { 488 // Check if the current tag should be handled. 489 if (desired_tags.count(static_cast<TiffDirectory::Tag>(tag)) != 1) { 490 continue; 491 } 492 } else { 493 return false; 494 } 495 496 const size_t type_size = SizeOfType(type, nullptr /* no error */); 497 498 // Check that type_size * number_of_elements does not exceed UINT32_MAX. 499 if (type_size != 0 && number_of_elements > UINT32_MAX / type_size) { 500 return false; 501 } 502 const size_t byte_count = 503 type_size * static_cast<size_t>(number_of_elements); 504 505 std::uint32_t value_offset; 506 if (byte_count > 4 && 507 Get32u(stream, ifd_offset + 10 + i, endian, &value_offset)) { 508 value_offset += tiff_offset; 509 } else if (byte_count != 0) { 510 value_offset = ifd_offset + 10 + i; 511 } else { 512 // Ignore entries with an invalid byte count. 513 continue; 514 } 515 516 Error error = kOk; 517 const std::vector<std::uint8_t> data = 518 GetData(value_offset, byte_count, stream, &error); 519 if (error != kOk) { 520 return false; 521 } 522 tiff_directory->AddEntry(tag, type, number_of_elements, value_offset, data); 523 } 524 525 return Get32u(stream, ifd_offset + 2u + number_of_entries * 12u, endian, 526 next_ifd_offset); 527 } 528 529 bool GetExifOrientation(StreamInterface* stream, const std::uint32_t offset, 530 std::uint32_t* orientation) { 531 const TagSet kOrientationTagSet = {kTiffTagOrientation}; 532 const std::uint32_t kNumberOfIfds = 1; 533 534 TiffContent tiff_content; 535 if (!TiffParser(stream, offset) 536 .Parse(kOrientationTagSet, kNumberOfIfds, &tiff_content)) { 537 return false; 538 } 539 540 for (const auto& tiff_directory : tiff_content.tiff_directory) { 541 if (tiff_directory.Has(kTiffTagOrientation) && 542 tiff_directory.Get(kTiffTagOrientation, orientation)) { 543 return true; 544 } 545 } 546 547 return false; 548 } 549 550 bool GetFullDimension32(const TiffDirectory& tiff_directory, 551 std::uint32_t* width, std::uint32_t* height) { 552 // The sub file type needs to be 0 (main image) to contain a valid full 553 // dimensions. This is important in particular for DNG. 554 if (tiff_directory.Has(kTiffTagSubFileType)) { 555 std::uint32_t sub_file_type; 556 if (!tiff_directory.Get(kTiffTagSubFileType, &sub_file_type) || 557 sub_file_type != 0) { 558 return false; 559 } 560 } 561 562 if (tiff_directory.Has(kExifTagDefaultCropSize)) { 563 if (!GetFullCropDimension(tiff_directory, width, height)) { 564 return false; 565 } 566 } else if (tiff_directory.Has(kExifTagWidth) && 567 tiff_directory.Has(kExifTagHeight)) { 568 if (!tiff_directory.Get(kExifTagWidth, width) || 569 !tiff_directory.Get(kExifTagHeight, height)) { 570 return false; 571 } 572 } else if (tiff_directory.Has(kTiffTagImageWidth) && 573 tiff_directory.Has(kTiffTagImageLength)) { 574 if (!tiff_directory.Get(kTiffTagImageWidth, width) || 575 !tiff_directory.Get(kTiffTagImageLength, height)) { 576 return false; 577 } 578 } else if (tiff_directory.Has(kPanaTagTopBorder) && 579 tiff_directory.Has(kPanaTagLeftBorder) && 580 tiff_directory.Has(kPanaTagBottomBorder) && 581 tiff_directory.Has(kPanaTagRightBorder)) { 582 std::uint32_t left; 583 std::uint32_t right; 584 std::uint32_t top; 585 std::uint32_t bottom; 586 if (tiff_directory.Get(kPanaTagLeftBorder, &left) && 587 tiff_directory.Get(kPanaTagRightBorder, &right) && 588 tiff_directory.Get(kPanaTagTopBorder, &top) && 589 tiff_directory.Get(kPanaTagBottomBorder, &bottom) && bottom > top && 590 right > left) { 591 *height = bottom - top; 592 *width = right - left; 593 } else { 594 return false; 595 } 596 } 597 return true; 598 } 599 600 bool GetFullCropDimension(const tiff_directory::TiffDirectory& tiff_directory, 601 std::uint32_t* width, std::uint32_t* height) { 602 if (!tiff_directory.Has(kExifTagDefaultCropSize)) { 603 // This doesn't look right to return true here, as we have not written 604 // anything to *width and *height. However, changing the return value here 605 // causes a whole bunch of tests to fail. 606 // TODO(timurrrr): Return false and fix the tests. 607 // In fact, this whole if() seems to be not needed, 608 // as tiff_directory(kExifTagDefaultCropSize) will return false below. 609 return true; 610 } 611 612 std::vector<std::uint32_t> crop(2); 613 if (tiff_directory.Get(kExifTagDefaultCropSize, &crop)) { 614 if (crop.size() == 2 && crop[0] > 0 && crop[1] > 0) { 615 *width = crop[0]; 616 *height = crop[1]; 617 return true; 618 } else { 619 return false; 620 } 621 } 622 623 std::vector<Rational> crop_rational(2); 624 if (tiff_directory.Get(kExifTagDefaultCropSize, &crop_rational)) { 625 if (crop_rational.size() == 2 && crop_rational[0].numerator > 0 && 626 crop_rational[0].denominator > 0 && crop_rational[1].numerator > 0 && 627 crop_rational[1].denominator > 0) { 628 *width = crop_rational[0].numerator / crop_rational[0].denominator; 629 *height = crop_rational[1].numerator / crop_rational[1].denominator; 630 return true; 631 } else { 632 return false; 633 } 634 } 635 636 return false; 637 } 638 639 TiffParser::TiffParser(StreamInterface* stream) : stream_(stream) {} 640 641 TiffParser::TiffParser(StreamInterface* stream, const std::uint32_t offset) 642 : stream_(stream), tiff_offset_(offset) {} 643 644 bool TiffParser::GetPreviewImageData(const TiffContent& tiff_content, 645 PreviewImageData* preview_image_data) { 646 bool success = true; 647 for (const auto& tiff_directory : tiff_content.tiff_directory) { 648 success = FillPreviewImageData(tiff_directory, stream_, preview_image_data); 649 if (success && tiff_directory.Has(kTiffTagExifIfd) && 650 tiff_content.exif_directory) { 651 success = FillPreviewImageData(*tiff_content.exif_directory, stream_, 652 preview_image_data); 653 } 654 if (success && tiff_directory.Has(kExifTagGps) && 655 tiff_content.gps_directory) { 656 FillGpsPreviewImageData(*tiff_content.gps_directory, preview_image_data); 657 } 658 for (const auto& sub_directory : tiff_directory.GetSubDirectories()) { 659 if (success) { 660 success = 661 FillPreviewImageData(sub_directory, stream_, preview_image_data); 662 } 663 } 664 } 665 return success; 666 } 667 668 bool TiffParser::Parse(const TagSet& desired_tags, 669 const std::uint16_t max_number_ifds, 670 TiffContent* tiff_content) { 671 if (!tiff_content->tiff_directory.empty()) { 672 return false; // You shall call Parse() only once. 673 } 674 675 const std::uint32_t kTiffIdentifierSize = 4; 676 std::uint32_t offset_to_ifd = 0; 677 if (!GetEndianness(tiff_offset_, stream_, &endian_) || 678 !Get32u(stream_, tiff_offset_ + kTiffIdentifierSize, endian_, 679 &offset_to_ifd)) { 680 return false; 681 } 682 683 if (!ParseIfd(tiff_offset_ + offset_to_ifd, desired_tags, max_number_ifds, 684 &tiff_content->tiff_directory)) { 685 return false; 686 } 687 688 // Get the Exif data. 689 if (FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory) != 690 nullptr) { 691 const TiffDirectory* tiff_ifd = 692 FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory); 693 std::uint32_t offset; 694 if (tiff_ifd->Get(kTiffTagExifIfd, &offset)) { 695 tiff_content->exif_directory.reset(new TiffDirectory(endian_)); 696 std::uint32_t next_ifd_offset; 697 if (!ParseDirectory( 698 tiff_offset_, tiff_offset_ + offset, endian_, desired_tags, 699 stream_, tiff_content->exif_directory.get(), &next_ifd_offset)) { 700 return false; 701 } 702 703 return ParseGpsData(tiff_ifd, tiff_content); 704 } 705 } 706 707 // Get the GPS data from the tiff ifd. 708 if (FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory) != 709 nullptr) { 710 const TiffDirectory* tiff_ifd = 711 FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory); 712 return ParseGpsData(tiff_ifd, tiff_content); 713 } 714 715 return true; 716 } 717 718 bool TiffParser::ParseIfd(const std::uint32_t offset_to_ifd, 719 const TagSet& desired_tags, 720 const std::uint16_t max_number_ifds, 721 IfdVector* tiff_directory) { 722 std::uint32_t next_ifd_offset; 723 TiffDirectory tiff_ifd(static_cast<Endian>(endian_)); 724 if (!ParseDirectory(tiff_offset_, offset_to_ifd, endian_, desired_tags, 725 stream_, &tiff_ifd, &next_ifd_offset) || 726 !ParseSubIfds(tiff_offset_, desired_tags, max_number_ifds, endian_, 727 stream_, &tiff_ifd)) { 728 return false; 729 } 730 731 tiff_directory->push_back(tiff_ifd); 732 if (next_ifd_offset != 0 && tiff_directory->size() < max_number_ifds) { 733 return ParseIfd(tiff_offset_ + next_ifd_offset, desired_tags, 734 max_number_ifds, tiff_directory); 735 } 736 return true; 737 } 738 739 bool TiffParser::ParseGpsData(const TiffDirectory* tiff_ifd, 740 TiffContent* tiff_content) { 741 std::uint32_t offset; 742 if (tiff_ifd->Get(kExifTagGps, &offset)) { 743 tiff_content->gps_directory.reset(new TiffDirectory(endian_)); 744 const TagSet gps_tags = {kGpsTagLatitudeRef, kGpsTagLatitude, 745 kGpsTagLongitudeRef, kGpsTagLongitude, 746 kGpsTagAltitudeRef, kGpsTagAltitude, 747 kGpsTagTimeStamp, kGpsTagDateStamp}; 748 std::uint32_t next_ifd_offset; 749 return ParseDirectory(tiff_offset_, tiff_offset_ + offset, endian_, 750 gps_tags, stream_, tiff_content->gps_directory.get(), 751 &next_ifd_offset); 752 } 753 return true; 754 } 755 756 } // namespace piex 757