tiff_parser.cc revision 29345531d776962073312d423917406fc59b09f6
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 Tags& 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 constexpr 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 jpeg_offset and jpeg_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 194 if (tiff_directory.Has(kTiffTagOrientation)) { 195 success &= tiff_directory.Get(kTiffTagOrientation, 196 &preview_image_data->exif_orientation); 197 } 198 199 // Get color_space 200 if (tiff_directory.Has(kExifTagColorSpace)) { 201 std::uint32_t color_space; 202 success &= tiff_directory.Get(kExifTagColorSpace, &color_space); 203 if (color_space == 1) { 204 preview_image_data->color_space = PreviewImageData::kSrgb; 205 } else if (color_space == 65535) { 206 preview_image_data->color_space = PreviewImageData::kAdobeRgb; 207 } 208 } 209 210 success &= GetFullDimension(tiff_directory, &preview_image_data->full_width, 211 &preview_image_data->full_height); 212 213 if (tiff_directory.Has(kTiffTagMake)) { 214 success &= tiff_directory.Get(kTiffTagMake, &preview_image_data->maker); 215 } 216 217 if (tiff_directory.Has(kTiffTagModel)) { 218 success &= tiff_directory.Get(kTiffTagModel, &preview_image_data->model); 219 } 220 221 if (tiff_directory.Has(kExifTagDateTimeOriginal)) { 222 success &= tiff_directory.Get(kExifTagDateTimeOriginal, 223 &preview_image_data->date_time); 224 } 225 226 if (tiff_directory.Has(kExifTagIsoSpeed)) { 227 success &= tiff_directory.Get(kExifTagIsoSpeed, &preview_image_data->iso); 228 } else if (tiff_directory.Has(kPanaTagIso)) { 229 success &= tiff_directory.Get(kPanaTagIso, &preview_image_data->iso); 230 } 231 232 if (tiff_directory.Has(kExifTagExposureTime)) { 233 success &= GetRational(kExifTagExposureTime, tiff_directory, 1, 234 &preview_image_data->exposure_time); 235 } 236 237 if (tiff_directory.Has(kExifTagFnumber)) { 238 success &= GetRational(kExifTagFnumber, tiff_directory, 1, 239 &preview_image_data->fnumber); 240 } 241 242 if (tiff_directory.Has(kExifTagFocalLength)) { 243 success &= GetRational(kExifTagFocalLength, tiff_directory, 1, 244 &preview_image_data->focal_length); 245 } 246 247 if (!success) { 248 return kFail; 249 } 250 251 return kOk; 252} 253 254const TiffDirectory* FindFirstTagInIfds(const Tags& tag, 255 const IfdVector& tiff_directory) { 256 for (std::uint32_t i = 0; i < tiff_directory.size(); ++i) { 257 if (tiff_directory[i].Has(tag)) { 258 return &tiff_directory[i]; 259 } 260 261 // Recursively search sub directories. 262 const TiffDirectory* sub_directory = 263 FindFirstTagInIfds(tag, tiff_directory[i].GetSubDirectories()); 264 if (sub_directory != NULL) { 265 return sub_directory; 266 } 267 } 268 return NULL; 269} 270 271// Gets the SubIfd content. 272void ParseSubIfds(const std::uint32_t tiff_offset, const TagSet& desired_tags, 273 const std::uint32_t max_number_ifds, const Endian endian, 274 StreamInterface* stream, TiffDirectory* tiff_ifd, 275 Error* error) { 276 if (*error == kOk && tiff_ifd->Has(kTiffTagSubIfd)) { 277 std::uint32_t offset = 0; 278 std::uint32_t length = 0; 279 tiff_ifd->GetOffsetAndLength(kTiffTagSubIfd, TIFF_TYPE_LONG, &offset, 280 &length); 281 length /= 4; // length in bytes divided by 4 gives number of IFDs. 282 for (std::uint32_t j = 0; j < length && j < max_number_ifds; ++j) { 283 std::uint32_t sub_offset; 284 if (!Get32u(stream, offset + 4 * j, endian, &sub_offset)) { 285 *error = kFail; 286 return; 287 } 288 289 std::uint32_t next_ifd_offset; 290 TiffDirectory sub_ifd(static_cast<Endian>(endian)); 291 *error = ParseDirectory(tiff_offset, sub_offset, endian, desired_tags, 292 stream, &sub_ifd, &next_ifd_offset); 293 if (*error != kOk) { 294 return; 295 } 296 297 tiff_ifd->AddSubDirectory(sub_ifd); 298 } 299 } 300} 301 302} // namespace 303 304bool Get16u(StreamInterface* stream, const std::uint32_t offset, 305 const Endian& endian, std::uint16_t* value) { 306 std::uint8_t data[2]; 307 if (stream->GetData(offset, 2, data) == kOk) { 308 if (endian == kBigEndian) { 309 *value = (data[0] * 0x100) | data[1]; 310 } else { 311 *value = (data[1] * 0x100) | data[0]; 312 } 313 return true; 314 } else { 315 return false; 316 } 317} 318 319bool Get32u(StreamInterface* stream, const std::uint32_t offset, 320 const Endian& endian, std::uint32_t* value) { 321 std::uint8_t data[4]; 322 if (stream->GetData(offset, 4, data) == kOk) { 323 if (endian == kBigEndian) { 324 *value = (data[0] * 0x1000000) | (data[1] * 0x10000) | (data[2] * 0x100) | 325 data[3]; 326 } else { 327 *value = (data[3] * 0x1000000) | (data[2] * 0x10000) | (data[1] * 0x100) | 328 data[0]; 329 } 330 return true; 331 } else { 332 return false; 333 } 334} 335 336std::vector<std::uint8_t> GetData(const size_t offset, const size_t length, 337 StreamInterface* stream, Error* error) { 338 // Read in chunks with a maximum size of 1 MiB. 339 const size_t kChunkSize = 1048576; 340 341 std::vector<std::uint8_t> data; 342 size_t processed_data = 0; 343 while (*error == kOk && processed_data < length) { 344 size_t chunk_length = kChunkSize; 345 if (length - data.size() < kChunkSize) { 346 chunk_length = length - data.size(); 347 } 348 349 data.resize(processed_data + chunk_length); 350 *error = stream->GetData(offset + processed_data, chunk_length, 351 &data[processed_data]); 352 353 processed_data += chunk_length; 354 } 355 return data; 356} 357 358bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream, 359 Endian* endian) { 360 const std::uint8_t kTiffBigEndianMagic[] = {'M', 'M'}; 361 const std::uint8_t kTiffLittleEndianMagic[] = {'I', 'I'}; 362 std::uint8_t tiff_endian[sizeof(kTiffBigEndianMagic)]; 363 if (stream->GetData(tiff_offset, sizeof(tiff_endian), &tiff_endian[0]) != 364 kOk) { 365 return false; 366 } 367 368 if (!memcmp(tiff_endian, kTiffLittleEndianMagic, sizeof(tiff_endian))) { 369 *endian = kLittleEndian; 370 return true; 371 } else if (!memcmp(tiff_endian, kTiffBigEndianMagic, sizeof(tiff_endian))) { 372 *endian = kBigEndian; 373 return true; 374 } else { 375 return false; 376 } 377} 378 379bool GetPreviewDimensions(const std::uint32_t jpeg_offset, 380 StreamInterface* stream, std::uint16_t* width, 381 std::uint16_t* height) { 382 const Endian endian = kBigEndian; 383 std::uint32_t offset = jpeg_offset; 384 std::uint16_t segment; 385 386 // Parse the JPEG header until we find Frame0 which contains the image width 387 // and height or the actual image data starts (StartOfScan) 388 do { 389 if (!Get16u(stream, offset, endian, &segment)) { 390 return false; 391 } 392 offset += 2; 393 394 switch (segment) { 395 case kStartOfImage: 396 break; 397 case kStartOfFrame: 398 return Get16u(stream, offset + 3, endian, height) && 399 Get16u(stream, offset + 5, endian, width); 400 default: { 401 std::uint16_t length; 402 if (!Get16u(stream, offset, endian, &length)) { 403 return false; 404 } 405 offset += length; 406 } 407 } 408 } while (segment != kStartOfScan); 409 410 // No width and hight information found. 411 return false; 412} 413 414Error ParseDirectory(const std::uint32_t tiff_offset, 415 const std::uint32_t ifd_offset, const Endian endian, 416 const TagSet& desired_tags, StreamInterface* stream, 417 TiffDirectory* tiff_directory, 418 std::uint32_t* next_ifd_offset) { 419 std::uint16_t number_of_entries; 420 if (!Get16u(stream, ifd_offset, endian, &number_of_entries)) { 421 return kFail; 422 } 423 424 for (std::uint32_t i = 0; 425 i < static_cast<std::uint32_t>(number_of_entries) * 12; i += 12) { 426 std::uint16_t tag; 427 std::uint16_t type; 428 std::uint32_t number_of_elements; 429 if (Get16u(stream, ifd_offset + 2 + i, endian, &tag) && 430 Get16u(stream, ifd_offset + 4 + i, endian, &type) && 431 Get32u(stream, ifd_offset + 6 + i, endian, &number_of_elements)) { 432 // Check if the current tag should be handled. 433 if (desired_tags.count(static_cast<Tags>(tag)) != 1) { 434 continue; 435 } 436 } else { 437 return kFail; 438 } 439 440 const size_t type_size = SizeOfType(type, nullptr /* no error */); 441 442 // Check that type_size * number_of_elements does not exceed UINT32_MAX. 443 if (type_size != 0 && number_of_elements > UINT32_MAX / type_size) { 444 return kFail; 445 } 446 const size_t byte_count = 447 type_size * static_cast<size_t>(number_of_elements); 448 449 std::uint32_t value_offset; 450 if (byte_count > 4 && 451 Get32u(stream, ifd_offset + 10 + i, endian, &value_offset)) { 452 value_offset += tiff_offset; 453 } else if (byte_count != 0) { 454 value_offset = ifd_offset + 10 + i; 455 } else { 456 // Ignore entries with an invalid byte count. 457 continue; 458 } 459 460 Error error = kOk; 461 const std::vector<std::uint8_t> data = 462 GetData(value_offset, byte_count, stream, &error); 463 if (error != kOk) { 464 return error; 465 } 466 tiff_directory->AddEntry(tag, type, number_of_elements, value_offset, data); 467 } 468 469 if (Get32u(stream, ifd_offset + 2 + number_of_entries * 12, endian, 470 next_ifd_offset)) { 471 return kOk; 472 } else { 473 return kFail; 474 } 475} 476 477TiffParser::TiffParser(StreamInterface* stream) : stream_(stream) {} 478 479TiffParser::TiffParser(StreamInterface* stream, const std::uint32_t offset) 480 : stream_(stream), tiff_offset_(offset) {} 481 482Error TiffParser::GetPreviewImageData(const TiffContent& tiff_content, 483 PreviewImageData* preview_image_data) { 484 Error error = kOk; 485 for (const auto& tiff_directory : tiff_content.tiff_directory) { 486 error = FillPreviewImageData(tiff_directory, preview_image_data); 487 if (error == kOk && tiff_directory.Has(kTiffTagExifIfd) && 488 tiff_content.exif_directory) { 489 error = FillPreviewImageData(*tiff_content.exif_directory, 490 preview_image_data); 491 } 492 if (error == kOk && tiff_directory.Has(kExifTagGps) && 493 tiff_content.gps_directory) { 494 FillGpsPreviewImageData(*tiff_content.gps_directory, preview_image_data); 495 } 496 for (const auto& sub_directory : tiff_directory.GetSubDirectories()) { 497 if (error == kOk) { 498 error = FillPreviewImageData(sub_directory, preview_image_data); 499 } 500 } 501 } 502 return error; 503} 504 505Error TiffParser::Parse(const TagSet& desired_tags, 506 const std::uint16_t max_number_ifds, 507 TiffContent* tiff_content) { 508 if (!tiff_content->tiff_directory.empty()) { 509 return kFail; // You shall call Parse() only once. 510 } 511 512 const std::uint32_t kTiffIdentifierSize = 4; 513 std::uint32_t offset_to_ifd = 0; 514 if (!GetEndianness(tiff_offset_, stream_, &endian_) || 515 !Get32u(stream_, tiff_offset_ + kTiffIdentifierSize, endian_, 516 &offset_to_ifd)) { 517 return kFail; 518 } 519 520 Error error = ParseIfd(tiff_offset_ + offset_to_ifd, desired_tags, 521 max_number_ifds, &tiff_content->tiff_directory); 522 if (error != kOk) { 523 return error; 524 } 525 526 // Get the Exif data. 527 const TiffDirectory* tiff_ifd = 528 FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory); 529 if (tiff_ifd != NULL) { 530 std::uint32_t offset; 531 if (tiff_ifd->Get(kTiffTagExifIfd, &offset)) { 532 tiff_content->exif_directory.reset(new TiffDirectory(endian_)); 533 std::uint32_t next_ifd_offset; 534 error = ParseDirectory( 535 tiff_offset_, tiff_offset_ + offset, endian_, desired_tags, stream_, 536 tiff_content->exif_directory.get(), &next_ifd_offset); 537 if (error != kOk) { 538 return error; 539 } 540 541 if (tiff_ifd->Get(kExifTagGps, &offset)) { 542 tiff_content->gps_directory.reset(new TiffDirectory(endian_)); 543 const TagSet gps_tags = {kGpsTagLatitudeRef, kGpsTagLatitude, 544 kGpsTagLongitudeRef, kGpsTagLongitude, 545 kGpsTagAltitudeRef, kGpsTagAltitude, 546 kGpsTagTimeStamp, kGpsTagDateStamp}; 547 return ParseDirectory( 548 tiff_offset_, tiff_offset_ + offset, endian_, gps_tags, stream_, 549 tiff_content->gps_directory.get(), &next_ifd_offset); 550 } 551 } 552 } 553 554 return error; 555} 556 557Error TiffParser::ParseIfd(const std::uint32_t offset_to_ifd, 558 const TagSet& desired_tags, 559 const std::uint16_t max_number_ifds, 560 IfdVector* tiff_directory) { 561 std::uint32_t next_ifd_offset; 562 TiffDirectory tiff_ifd(static_cast<Endian>(endian_)); 563 Error error = 564 ParseDirectory(tiff_offset_, offset_to_ifd, endian_, desired_tags, 565 stream_, &tiff_ifd, &next_ifd_offset); 566 567 ParseSubIfds(tiff_offset_, desired_tags, max_number_ifds, endian_, stream_, 568 &tiff_ifd, &error); 569 if (error == kOk) { 570 tiff_directory->push_back(tiff_ifd); 571 if (next_ifd_offset != 0 && tiff_directory->size() < max_number_ifds) { 572 error = ParseIfd(tiff_offset_ + next_ifd_offset, desired_tags, 573 max_number_ifds, tiff_directory); 574 } 575 } 576 577 return error; 578} 579 580} // namespace piex 581