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