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