1// Copyright 2015 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15////////////////////////////////////////////////////////////////////////////////
16
17#include "src/tiff_parser.h"
18
19#include <cstring>
20#include <limits>
21#include <numeric>
22
23#include "src/tiff_directory/tiff_directory.h"
24
25namespace piex {
26namespace {
27
28using tiff_directory::Endian;
29using tiff_directory::Rational;
30using tiff_directory::SRational;
31using tiff_directory::SizeOfType;
32using tiff_directory::TIFF_TYPE_LONG;
33using tiff_directory::TIFF_TYPE_UNDEFINED;
34using tiff_directory::TiffDirectory;
35using tiff_directory::kBigEndian;
36using tiff_directory::kLittleEndian;
37
38// Specifies all tags that might be of interest to parse JPEG data.
39const std::uint32_t kStartOfFrame = 0xFFC0;
40const std::uint32_t kStartOfImage = 0xFFD8;
41const std::uint32_t kStartOfScan = 0xFFDA;
42
43bool GetFullDimension16(const TiffDirectory& tiff_directory,
44                        std::uint16_t* width, std::uint16_t* height) {
45  std::uint32_t tmp_width = 0;
46  std::uint32_t tmp_height = 0;
47  if (!GetFullDimension32(tiff_directory, &tmp_width, &tmp_height) ||
48      tmp_width > std::numeric_limits<std::uint16_t>::max() ||
49      tmp_height > std::numeric_limits<std::uint16_t>::max()) {
50    return false;
51  }
52  *width = static_cast<std::uint16_t>(tmp_width);
53  *height = static_cast<std::uint16_t>(tmp_height);
54  return true;
55}
56
57bool GetRational(const TiffDirectory::Tag& tag, const TiffDirectory& directory,
58                 const int data_size, PreviewImageData::Rational* data) {
59  std::vector<Rational> value;
60  if (directory.Get(tag, &value) &&
61      value.size() == static_cast<size_t>(data_size)) {
62    for (size_t i = 0; i < value.size(); ++i) {
63      data[i].numerator = value[i].numerator;
64      data[i].denominator = value[i].denominator;
65    }
66    return true;
67  }
68  return false;
69}
70
71void FillGpsPreviewImageData(const TiffDirectory& gps_directory,
72                             PreviewImageData* preview_image_data) {
73  if (gps_directory.Has(kGpsTagLatitudeRef) &&
74      gps_directory.Has(kGpsTagLatitude) &&
75      gps_directory.Has(kGpsTagLongitudeRef) &&
76      gps_directory.Has(kGpsTagLongitude) &&
77      gps_directory.Has(kGpsTagTimeStamp) &&
78      gps_directory.Has(kGpsTagDateStamp)) {
79    preview_image_data->gps.is_valid = false;
80    std::string value;
81    if (!gps_directory.Get(kGpsTagLatitudeRef, &value) || value.empty() ||
82        (value[0] != 'N' && value[0] != 'S') ||
83        !GetRational(kGpsTagLatitude, gps_directory, 3 /* data size */,
84                     preview_image_data->gps.latitude)) {
85      return;
86    }
87    preview_image_data->gps.latitude_ref = value[0];
88
89    if (!gps_directory.Get(kGpsTagLongitudeRef, &value) || value.empty() ||
90        (value[0] != 'E' && value[0] != 'W') ||
91        !GetRational(kGpsTagLongitude, gps_directory, 3 /* data size */,
92                     preview_image_data->gps.longitude)) {
93      return;
94    }
95    preview_image_data->gps.longitude_ref = value[0];
96
97    if (!GetRational(kGpsTagTimeStamp, gps_directory, 3 /* data size */,
98                     preview_image_data->gps.time_stamp)) {
99      return;
100    }
101
102    const size_t kGpsDateStampSize = 11;
103    if (!gps_directory.Get(kGpsTagDateStamp,
104                           &preview_image_data->gps.date_stamp)) {
105      return;
106    }
107    if (preview_image_data->gps.date_stamp.size() == kGpsDateStampSize) {
108      // Resize the date_stamp to remove the "NULL" at the end of string.
109      preview_image_data->gps.date_stamp.resize(kGpsDateStampSize - 1);
110    } else {
111      return;
112    }
113
114    if (gps_directory.Has(kGpsTagAltitudeRef) &&
115        gps_directory.Has(kGpsTagAltitude)) {
116      std::vector<std::uint8_t> bytes;
117      if (!gps_directory.Get(kGpsTagAltitudeRef, &bytes) || bytes.empty() ||
118          !GetRational(kGpsTagAltitude, gps_directory, 1,
119                       &preview_image_data->gps.altitude)) {
120        return;
121      }
122      preview_image_data->gps.altitude_ref = bytes[0] != 0;
123    }
124    preview_image_data->gps.is_valid = true;
125  }
126}
127
128void GetImageSize(const TiffDirectory& tiff_directory, StreamInterface* stream,
129                  Image* image) {
130  switch (image->format) {
131    case Image::kUncompressedRgb: {
132      GetFullDimension16(tiff_directory, &image->width, &image->height);
133      break;
134    }
135    case Image::kJpegCompressed: {
136      GetJpegDimensions(image->offset, stream, &image->width, &image->height);
137      break;
138    }
139    default: { return; }
140  }
141}
142
143bool FillPreviewImageData(const TiffDirectory& tiff_directory,
144                          StreamInterface* stream,
145                          PreviewImageData* preview_image_data) {
146  bool success = true;
147  // Get preview or thumbnail. The code assumes that only thumbnails can be
148  // uncompressed. Preview images are always JPEG compressed.
149  Image image;
150  if (GetImageData(tiff_directory, stream, &image)) {
151    if (IsThumbnail(image)) {
152      preview_image_data->thumbnail = image;
153    } else if (image.format == Image::kJpegCompressed) {
154      preview_image_data->preview = image;
155    }
156  }
157
158  // Get exif_orientation if it was not set already.
159  if (tiff_directory.Has(kTiffTagOrientation) &&
160      preview_image_data->exif_orientation == 1) {
161    success &= tiff_directory.Get(kTiffTagOrientation,
162                                  &preview_image_data->exif_orientation);
163  }
164
165  // Get color_space
166  if (tiff_directory.Has(kExifTagColorSpace)) {
167    std::uint32_t color_space;
168    success &= tiff_directory.Get(kExifTagColorSpace, &color_space);
169    if (color_space == 1) {
170      preview_image_data->color_space = PreviewImageData::kSrgb;
171    } else if (color_space == 65535 || color_space == 2) {
172      preview_image_data->color_space = PreviewImageData::kAdobeRgb;
173    }
174  }
175
176  success &= GetFullDimension32(tiff_directory, &preview_image_data->full_width,
177                                &preview_image_data->full_height);
178
179  if (tiff_directory.Has(kTiffTagMake)) {
180    success &= tiff_directory.Get(kTiffTagMake, &preview_image_data->maker);
181  }
182
183  if (tiff_directory.Has(kTiffTagModel)) {
184    success &= tiff_directory.Get(kTiffTagModel, &preview_image_data->model);
185  }
186
187  if (tiff_directory.Has(kTiffTagCfaPatternDim)) {
188    std::vector<std::uint32_t> cfa_pattern_dim;
189    if (tiff_directory.Get(kTiffTagCfaPatternDim, &cfa_pattern_dim) &&
190        cfa_pattern_dim.size() == 2) {
191      preview_image_data->cfa_pattern_dim[0] = cfa_pattern_dim[0];
192      preview_image_data->cfa_pattern_dim[1] = cfa_pattern_dim[1];
193    }
194  }
195
196  if (tiff_directory.Has(kExifTagDateTimeOriginal)) {
197    success &= tiff_directory.Get(kExifTagDateTimeOriginal,
198                                  &preview_image_data->date_time);
199  }
200
201  if (tiff_directory.Has(kExifTagIsoSpeed)) {
202    success &= tiff_directory.Get(kExifTagIsoSpeed, &preview_image_data->iso);
203  } else if (tiff_directory.Has(kPanaTagIso)) {
204    success &= tiff_directory.Get(kPanaTagIso, &preview_image_data->iso);
205  }
206
207  if (tiff_directory.Has(kExifTagExposureTime)) {
208    success &= GetRational(kExifTagExposureTime, tiff_directory, 1,
209                           &preview_image_data->exposure_time);
210  }
211
212  if (tiff_directory.Has(kExifTagFnumber)) {
213    success &= GetRational(kExifTagFnumber, tiff_directory, 1,
214                           &preview_image_data->fnumber);
215  }
216
217  if (tiff_directory.Has(kExifTagFocalLength)) {
218    success &= GetRational(kExifTagFocalLength, tiff_directory, 1,
219                           &preview_image_data->focal_length);
220  }
221
222  return success;
223}
224
225const TiffDirectory* FindFirstTagInIfds(const TiffDirectory::Tag& tag,
226                                        const IfdVector& tiff_directory) {
227  for (std::uint32_t i = 0; i < tiff_directory.size(); ++i) {
228    if (tiff_directory[i].Has(tag)) {
229      return &tiff_directory[i];
230    }
231
232    // Recursively search sub directories.
233    const TiffDirectory* sub_directory =
234        FindFirstTagInIfds(tag, tiff_directory[i].GetSubDirectories());
235    if (sub_directory != NULL) {
236      return sub_directory;
237    }
238  }
239  return NULL;
240}
241
242// Return true if all data blocks are ordered one after the other without gaps.
243bool OffsetsAreConsecutive(
244    const std::vector<std::uint32_t>& strip_offsets,
245    const std::vector<std::uint32_t>& strip_byte_counts) {
246  if (strip_offsets.size() != strip_byte_counts.size() ||
247      strip_offsets.empty()) {
248    return false;
249  }
250
251  for (size_t i = 0; i < strip_offsets.size() - 1; ++i) {
252    if (strip_offsets[i] + strip_byte_counts[i] != strip_offsets[i + 1]) {
253      return false;
254    }
255  }
256  return true;
257}
258
259// Gets the SubIfd content.
260bool ParseSubIfds(const std::uint32_t tiff_offset, const TagSet& desired_tags,
261                  const std::uint32_t max_number_ifds, const Endian endian,
262                  StreamInterface* stream, TiffDirectory* tiff_ifd) {
263  if (tiff_ifd->Has(kTiffTagSubIfd)) {
264    std::uint32_t offset = 0;
265    std::uint32_t length = 0;
266    tiff_ifd->GetOffsetAndLength(kTiffTagSubIfd, TIFF_TYPE_LONG, &offset,
267                                 &length);
268    length /= 4;  // length in bytes divided by 4 gives number of IFDs.
269    for (std::uint32_t j = 0; j < length && j < max_number_ifds; ++j) {
270      std::uint32_t sub_offset;
271      if (!Get32u(stream, offset + 4 * j, endian, &sub_offset)) {
272        return false;
273      }
274
275      std::uint32_t next_ifd_offset;
276      TiffDirectory sub_ifd(static_cast<Endian>(endian));
277      if (!ParseDirectory(tiff_offset, sub_offset, endian, desired_tags, stream,
278                          &sub_ifd, &next_ifd_offset)) {
279        return false;
280      }
281
282      tiff_ifd->AddSubDirectory(sub_ifd);
283    }
284  }
285  return true;
286}
287
288}  // namespace
289
290bool Get16u(StreamInterface* stream, const std::uint32_t offset,
291            const Endian& endian, std::uint16_t* value) {
292  std::uint8_t data[2];
293  if (stream->GetData(offset, 2, data) == kOk) {
294    if (endian == kBigEndian) {
295      *value = (data[0] * 0x100) | data[1];
296    } else {
297      *value = (data[1] * 0x100) | data[0];
298    }
299    return true;
300  } else {
301    return false;
302  }
303}
304
305bool Get32u(StreamInterface* stream, const std::uint32_t offset,
306            const Endian& endian, std::uint32_t* value) {
307  std::uint8_t data[4];
308  if (stream->GetData(offset, 4, data) == kOk) {
309    if (endian == kBigEndian) {
310      *value = (data[0] * 0x1000000u) | (data[1] * 0x10000u) |
311               (data[2] * 0x100u) | data[3];
312    } else {
313      *value = (data[3] * 0x1000000u) | (data[2] * 0x10000u) |
314               (data[1] * 0x100u) | data[0];
315    }
316    return true;
317  } else {
318    return false;
319  }
320}
321
322std::vector<std::uint8_t> GetData(const size_t offset, const size_t length,
323                                  StreamInterface* stream, Error* error) {
324  // Read in chunks with a maximum size of 1 MiB.
325  const size_t kChunkSize = 1048576;
326
327  std::vector<std::uint8_t> data;
328  size_t processed_data = 0;
329  while (*error == kOk && processed_data < length) {
330    size_t chunk_length = kChunkSize;
331    if (length - data.size() < kChunkSize) {
332      chunk_length = length - data.size();
333    }
334
335    data.resize(processed_data + chunk_length);
336    *error = stream->GetData(offset + processed_data, chunk_length,
337                             &data[processed_data]);
338
339    processed_data += chunk_length;
340  }
341  return data;
342}
343
344bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream,
345                   Endian* endian) {
346  const std::uint8_t kTiffBigEndianMagic[] = {'M', 'M'};
347  const std::uint8_t kTiffLittleEndianMagic[] = {'I', 'I'};
348  std::uint8_t tiff_endian[sizeof(kTiffBigEndianMagic)];
349  if (stream->GetData(tiff_offset, sizeof(tiff_endian), &tiff_endian[0]) !=
350      kOk) {
351    return false;
352  }
353
354  if (!memcmp(tiff_endian, kTiffLittleEndianMagic, sizeof(tiff_endian))) {
355    *endian = kLittleEndian;
356    return true;
357  } else if (!memcmp(tiff_endian, kTiffBigEndianMagic, sizeof(tiff_endian))) {
358    *endian = kBigEndian;
359    return true;
360  } else {
361    return false;
362  }
363}
364
365bool GetImageData(const TiffDirectory& tiff_directory, StreamInterface* stream,
366                  Image* image) {
367  std::uint32_t length = 0;
368  std::uint32_t offset = 0;
369
370  if (tiff_directory.Has(kTiffTagJpegOffset) &&
371      tiff_directory.Has(kTiffTagJpegByteCount)) {
372    if (!tiff_directory.Get(kTiffTagJpegOffset, &offset) ||
373        !tiff_directory.Get(kTiffTagJpegByteCount, &length)) {
374      return false;
375    }
376    image->format = Image::kJpegCompressed;
377  } else if (tiff_directory.Has(kTiffTagStripOffsets) &&
378             tiff_directory.Has(kTiffTagStripByteCounts)) {
379    std::vector<std::uint32_t> strip_offsets;
380    std::vector<std::uint32_t> strip_byte_counts;
381    if (!tiff_directory.Get(kTiffTagStripOffsets, &strip_offsets) ||
382        !tiff_directory.Get(kTiffTagStripByteCounts, &strip_byte_counts)) {
383      return false;
384    }
385
386    std::uint32_t compression = 0;
387    if (!OffsetsAreConsecutive(strip_offsets, strip_byte_counts) ||
388        !tiff_directory.Get(kTiffTagCompression, &compression)) {
389      return false;
390    }
391
392    std::uint32_t photometric_interpretation = 0;
393    if (tiff_directory.Get(kTiffTagPhotometric, &photometric_interpretation) &&
394        photometric_interpretation != 2 /* RGB */ &&
395        photometric_interpretation != 6 /* YCbCr */) {
396      return false;
397    }
398
399    switch (compression) {
400      case 1: /*uncompressed*/
401        image->format = Image::kUncompressedRgb;
402        break;
403      case 6: /* JPEG(old) */
404      case 7: /* JPEG */
405        image->format = Image::kJpegCompressed;
406        break;
407      default:
408        return false;
409    }
410    length = static_cast<std::uint32_t>(
411        std::accumulate(strip_byte_counts.begin(), strip_byte_counts.end(), 0));
412    offset = strip_offsets[0];
413  } else if (tiff_directory.Has(kPanaTagJpegImage)) {
414    if (!tiff_directory.GetOffsetAndLength(
415            kPanaTagJpegImage, TIFF_TYPE_UNDEFINED, &offset, &length)) {
416      return false;
417    }
418    image->format = Image::kJpegCompressed;
419  } else {
420    return false;
421  }
422
423  image->length = length;
424  image->offset = offset;
425  GetImageSize(tiff_directory, stream, image);
426  return true;
427}
428
429bool GetJpegDimensions(const std::uint32_t jpeg_offset, StreamInterface* stream,
430                       std::uint16_t* width, std::uint16_t* height) {
431  const Endian endian = kBigEndian;
432  std::uint32_t offset = jpeg_offset;
433  std::uint16_t segment;
434
435  // Parse the JPEG header until we find Frame0 which contains the image width
436  // and height or the actual image data starts (StartOfScan)
437  do {
438    if (!Get16u(stream, offset, endian, &segment)) {
439      return false;
440    }
441    offset += 2;
442
443    switch (segment) {
444      case kStartOfImage:
445        break;
446      case kStartOfFrame:
447        return Get16u(stream, offset + 3, endian, height) &&
448               Get16u(stream, offset + 5, endian, width);
449      default: {
450        std::uint16_t length;
451        if (!Get16u(stream, offset, endian, &length)) {
452          return false;
453        }
454        offset += length;
455      }
456    }
457  } while (segment != kStartOfScan);
458
459  // No width and hight information found.
460  return false;
461}
462
463bool IsThumbnail(const Image& image, const int max_dimension) {
464  return image.width <= max_dimension && image.height <= max_dimension;
465}
466
467bool ParseDirectory(const std::uint32_t tiff_offset,
468                    const std::uint32_t ifd_offset, const Endian endian,
469                    const TagSet& desired_tags, StreamInterface* stream,
470                    TiffDirectory* tiff_directory,
471                    std::uint32_t* next_ifd_offset) {
472  std::uint16_t number_of_entries;
473  if (!Get16u(stream, ifd_offset, endian, &number_of_entries)) {
474    return false;
475  }
476
477  for (std::uint32_t i = 0;
478       i < static_cast<std::uint32_t>(number_of_entries) * 12; i += 12) {
479    std::uint16_t tag;
480    std::uint16_t type;
481    std::uint32_t number_of_elements;
482    if (Get16u(stream, ifd_offset + 2 + i, endian, &tag) &&
483        Get16u(stream, ifd_offset + 4 + i, endian, &type) &&
484        Get32u(stream, ifd_offset + 6 + i, endian, &number_of_elements)) {
485      // Check if the current tag should be handled.
486      if (desired_tags.count(static_cast<TiffDirectory::Tag>(tag)) != 1) {
487        continue;
488      }
489    } else {
490      return false;
491    }
492
493    const size_t type_size = SizeOfType(type, nullptr /* no error */);
494
495    // Check that type_size * number_of_elements does not exceed UINT32_MAX.
496    if (type_size != 0 && number_of_elements > UINT32_MAX / type_size) {
497      return false;
498    }
499    const size_t byte_count =
500        type_size * static_cast<size_t>(number_of_elements);
501
502    std::uint32_t value_offset;
503    if (byte_count > 4 &&
504        Get32u(stream, ifd_offset + 10 + i, endian, &value_offset)) {
505      value_offset += tiff_offset;
506    } else if (byte_count != 0) {
507      value_offset = ifd_offset + 10 + i;
508    } else {
509      // Ignore entries with an invalid byte count.
510      continue;
511    }
512
513    Error error = kOk;
514    const std::vector<std::uint8_t> data =
515        GetData(value_offset, byte_count, stream, &error);
516    if (error != kOk) {
517      return false;
518    }
519    tiff_directory->AddEntry(tag, type, number_of_elements, value_offset, data);
520  }
521
522  return Get32u(stream, ifd_offset + 2u + number_of_entries * 12u, endian,
523                next_ifd_offset);
524}
525
526bool GetExifOrientation(StreamInterface* stream, const std::uint32_t offset,
527                        std::uint32_t* orientation) {
528  const TagSet kOrientationTagSet = {kTiffTagOrientation};
529  const std::uint32_t kNumberOfIfds = 1;
530
531  TiffContent tiff_content;
532  if (!TiffParser(stream, offset)
533           .Parse(kOrientationTagSet, kNumberOfIfds, &tiff_content)) {
534    return false;
535  }
536
537  for (const auto& tiff_directory : tiff_content.tiff_directory) {
538    if (tiff_directory.Has(kTiffTagOrientation) &&
539        tiff_directory.Get(kTiffTagOrientation, orientation)) {
540      return true;
541    }
542  }
543
544  return false;
545}
546
547bool GetFullDimension32(const TiffDirectory& tiff_directory,
548                        std::uint32_t* width, std::uint32_t* height) {
549  // The sub file type needs to be 0 (main image) to contain a valid full
550  // dimensions. This is important in particular for DNG.
551  if (tiff_directory.Has(kTiffTagSubFileType)) {
552    std::uint32_t sub_file_type;
553    if (!tiff_directory.Get(kTiffTagSubFileType, &sub_file_type) ||
554        sub_file_type != 0) {
555      return false;
556    }
557  }
558
559  if (tiff_directory.Has(kExifTagDefaultCropSize)) {
560    if (!GetFullCropDimension(tiff_directory, width, height)) {
561      return false;
562    }
563  } else if (tiff_directory.Has(kExifTagWidth) &&
564             tiff_directory.Has(kExifTagHeight)) {
565    if (!tiff_directory.Get(kExifTagWidth, width) ||
566        !tiff_directory.Get(kExifTagHeight, height)) {
567      return false;
568    }
569  } else if (tiff_directory.Has(kTiffTagImageWidth) &&
570             tiff_directory.Has(kTiffTagImageLength)) {
571    if (!tiff_directory.Get(kTiffTagImageWidth, width) ||
572        !tiff_directory.Get(kTiffTagImageLength, height)) {
573      return false;
574    }
575  } else if (tiff_directory.Has(kPanaTagTopBorder) &&
576             tiff_directory.Has(kPanaTagLeftBorder) &&
577             tiff_directory.Has(kPanaTagBottomBorder) &&
578             tiff_directory.Has(kPanaTagRightBorder)) {
579    std::uint32_t left;
580    std::uint32_t right;
581    std::uint32_t top;
582    std::uint32_t bottom;
583    if (tiff_directory.Get(kPanaTagLeftBorder, &left) &&
584        tiff_directory.Get(kPanaTagRightBorder, &right) &&
585        tiff_directory.Get(kPanaTagTopBorder, &top) &&
586        tiff_directory.Get(kPanaTagBottomBorder, &bottom) && bottom > top &&
587        right > left) {
588      *height = bottom - top;
589      *width = right - left;
590    } else {
591      return false;
592    }
593  }
594  return true;
595}
596
597bool GetFullCropDimension(const tiff_directory::TiffDirectory& tiff_directory,
598                          std::uint32_t* width, std::uint32_t* height) {
599  if (tiff_directory.Has(kExifTagDefaultCropSize)) {
600    std::vector<std::uint32_t> crop(2);
601    std::vector<Rational> crop_rational(2);
602    if (tiff_directory.Get(kExifTagDefaultCropSize, &crop)) {
603      *width = crop[0];
604      *height = crop[1];
605    } else if (tiff_directory.Get(kExifTagDefaultCropSize, &crop_rational) &&
606               crop_rational[0].denominator != 0 &&
607               crop_rational[1].denominator != 0) {
608      *width = crop_rational[0].numerator / crop_rational[0].denominator;
609      *height = crop_rational[1].numerator / crop_rational[1].denominator;
610    } else {
611      return false;
612    }
613  }
614
615  return true;
616}
617
618TiffParser::TiffParser(StreamInterface* stream) : stream_(stream) {}
619
620TiffParser::TiffParser(StreamInterface* stream, const std::uint32_t offset)
621    : stream_(stream), tiff_offset_(offset) {}
622
623bool TiffParser::GetPreviewImageData(const TiffContent& tiff_content,
624                                     PreviewImageData* preview_image_data) {
625  bool success = true;
626  for (const auto& tiff_directory : tiff_content.tiff_directory) {
627    success = FillPreviewImageData(tiff_directory, stream_, preview_image_data);
628    if (success && tiff_directory.Has(kTiffTagExifIfd) &&
629        tiff_content.exif_directory) {
630      success = FillPreviewImageData(*tiff_content.exif_directory, stream_,
631                                     preview_image_data);
632    }
633    if (success && tiff_directory.Has(kExifTagGps) &&
634        tiff_content.gps_directory) {
635      FillGpsPreviewImageData(*tiff_content.gps_directory, preview_image_data);
636    }
637    for (const auto& sub_directory : tiff_directory.GetSubDirectories()) {
638      if (success) {
639        success =
640            FillPreviewImageData(sub_directory, stream_, preview_image_data);
641      }
642    }
643  }
644  return success;
645}
646
647bool TiffParser::Parse(const TagSet& desired_tags,
648                       const std::uint16_t max_number_ifds,
649                       TiffContent* tiff_content) {
650  if (!tiff_content->tiff_directory.empty()) {
651    return false;  // You shall call Parse() only once.
652  }
653
654  const std::uint32_t kTiffIdentifierSize = 4;
655  std::uint32_t offset_to_ifd = 0;
656  if (!GetEndianness(tiff_offset_, stream_, &endian_) ||
657      !Get32u(stream_, tiff_offset_ + kTiffIdentifierSize, endian_,
658              &offset_to_ifd)) {
659    return false;
660  }
661
662  if (!ParseIfd(tiff_offset_ + offset_to_ifd, desired_tags, max_number_ifds,
663                &tiff_content->tiff_directory)) {
664    return false;
665  }
666
667  // Get the Exif data.
668  if (FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory) !=
669      nullptr) {
670    const TiffDirectory* tiff_ifd =
671        FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory);
672    std::uint32_t offset;
673    if (tiff_ifd->Get(kTiffTagExifIfd, &offset)) {
674      tiff_content->exif_directory.reset(new TiffDirectory(endian_));
675      std::uint32_t next_ifd_offset;
676      if (!ParseDirectory(
677              tiff_offset_, tiff_offset_ + offset, endian_, desired_tags,
678              stream_, tiff_content->exif_directory.get(), &next_ifd_offset)) {
679        return false;
680      }
681
682      return ParseGpsData(tiff_ifd, tiff_content);
683    }
684  }
685
686  // Get the GPS data from the tiff ifd.
687  if (FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory) !=
688      nullptr) {
689    const TiffDirectory* tiff_ifd =
690        FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory);
691    return ParseGpsData(tiff_ifd, tiff_content);
692  }
693
694  return true;
695}
696
697bool TiffParser::ParseIfd(const std::uint32_t offset_to_ifd,
698                          const TagSet& desired_tags,
699                          const std::uint16_t max_number_ifds,
700                          IfdVector* tiff_directory) {
701  std::uint32_t next_ifd_offset;
702  TiffDirectory tiff_ifd(static_cast<Endian>(endian_));
703  if (!ParseDirectory(tiff_offset_, offset_to_ifd, endian_, desired_tags,
704                      stream_, &tiff_ifd, &next_ifd_offset) ||
705      !ParseSubIfds(tiff_offset_, desired_tags, max_number_ifds, endian_,
706                    stream_, &tiff_ifd)) {
707    return false;
708  }
709
710  tiff_directory->push_back(tiff_ifd);
711  if (next_ifd_offset != 0 && tiff_directory->size() < max_number_ifds) {
712    return ParseIfd(tiff_offset_ + next_ifd_offset, desired_tags,
713                    max_number_ifds, tiff_directory);
714  }
715  return true;
716}
717
718bool TiffParser::ParseGpsData(const TiffDirectory* tiff_ifd,
719                              TiffContent* tiff_content) {
720  std::uint32_t offset;
721  if (tiff_ifd->Get(kExifTagGps, &offset)) {
722    tiff_content->gps_directory.reset(new TiffDirectory(endian_));
723    const TagSet gps_tags = {kGpsTagLatitudeRef,  kGpsTagLatitude,
724                             kGpsTagLongitudeRef, kGpsTagLongitude,
725                             kGpsTagAltitudeRef,  kGpsTagAltitude,
726                             kGpsTagTimeStamp,    kGpsTagDateStamp};
727    std::uint32_t next_ifd_offset;
728    return ParseDirectory(tiff_offset_, tiff_offset_ + offset, endian_,
729                          gps_tags, stream_, tiff_content->gps_directory.get(),
730                          &next_ifd_offset);
731  }
732  return true;
733}
734
735}  // namespace piex
736