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