Home | History | Annotate | Download | only in src
      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