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#ifndef PIEX_TIFF_PARSER_H_
18#define PIEX_TIFF_PARSER_H_
19
20#include <cstdint>
21#include <memory>
22#include <set>
23#include <vector>
24
25#include "src/piex_types.h"
26#include "src/tiff_directory/tiff_directory.h"
27
28namespace piex {
29
30// Specifies the maximum number of pixels for thumbnails in each direction.
31const int kThumbnailMaxDimension = 512;
32
33// Specifies all tags that might be of interest to get the preview data.
34enum GpsTags {
35 kGpsTagLatitudeRef = 1,
36 kGpsTagLatitude = 2,
37 kGpsTagLongitudeRef = 3,
38 kGpsTagLongitude = 4,
39 kGpsTagAltitudeRef = 5,
40 kGpsTagAltitude = 6,
41 kGpsTagTimeStamp = 7,
42 kGpsTagDateStamp = 29,
43};
44
45enum TiffTags {
46 kExifTagColorSpace = 0xA001,
47 kExifTagDateTimeOriginal = 0x9003,
48 kExifTagDefaultCropSize = 0xC620,
49 kExifTagExposureTime = 0x829a,
50 kExifTagFnumber = 0x829d,
51 kExifTagFocalLength = 0x920A,
52 kExifTagGps = 0x8825,
53 kExifTagHeight = 0xA003,
54 kExifTagIsoSpeed = 0x8827,
55 kExifTagMakernotes = 0x927C,
56 kExifTagWidth = 0xA002,
57 kOlymTagAspectFrame = 0x1113,
58 kOlymTagCameraSettings = 0x2020,
59 kOlymTagRawProcessing = 0x2040,
60 kPanaTagBottomBorder = 0x006,
61 kPanaTagIso = 0x0017,
62 kPanaTagJpegImage = 0x002E,
63 kPanaTagLeftBorder = 0x0005,
64 kPanaTagRightBorder = 0x007,
65 kPanaTagTopBorder = 0x0004,
66 kPentaxTagColorSpace = 0x0037,
67 kTiffTagArtist = 0x013B,
68 kTiffTagBitsPerSample = 0x0102,
69 kTiffTagCfaPatternDim = 0x828D,
70 kTiffTagCompression = 0x0103,
71 kTiffTagDateTime = 0x0132,
72 kTiffTagExifIfd = 0x8769,
73 kTiffTagImageDescription = 0x010E,
74 kTiffTagImageLength = 0x0101,
75 kTiffTagImageWidth = 0x0100,
76 kTiffTagJpegByteCount = 0x0202,
77 kTiffTagJpegOffset = 0x0201,
78 kTiffTagMake = 0x010F,
79 kTiffTagModel = 0x0110,
80 kTiffTagOrientation = 0x0112,
81 kTiffTagPhotometric = 0x0106,
82 kTiffTagPlanarConfig = 0x011C,
83 kTiffTagResolutionUnit = 0x0128,
84 kTiffTagRowsPerStrip = 0x0116,
85 kTiffTagSamplesPerPixel = 0x0115,
86 kTiffTagSoftware = 0x0131,
87 kTiffTagStripByteCounts = 0x0117,
88 kTiffTagStripOffsets = 0x0111,
89 kTiffTagSubFileType = 0x00FE,
90 kTiffTagSubIfd = 0x014A,
91 kTiffTagTileByteCounts = 0x0145,
92 kTiffTagTileLength = 0x0143,
93 kTiffTagTileOffsets = 0x0144,
94 kTiffTagTileWidth = 0x0142,
95 kTiffTagXresolution = 0x011A,
96 kTiffTagYresolution = 0x011B,
97};
98
99typedef std::set<tiff_directory::TiffDirectory::Tag> TagSet;
100typedef std::vector<tiff_directory::TiffDirectory> IfdVector;
101
102struct TiffContent {
103 IfdVector tiff_directory;
104 std::unique_ptr<tiff_directory::TiffDirectory> exif_directory;
105 std::unique_ptr<tiff_directory::TiffDirectory> gps_directory;
106};
107
108// Reads 2 bytes, an unsigned 16bit from 'stream' at a certain 'offset'. The
109// bytes get swapped according to the desired endianness returning true on
110// success. Returns false when something is wrong.
111bool Get16u(StreamInterface* stream, const std::uint32_t offset,
112 const tiff_directory::Endian& endian, std::uint16_t* value);
113
114// Reads 4 bytes, an unsigned 32bit 'value' from 'stream' at a certain 'offset'.
115// The bytes get swapped according to the desired endianness returning true on
116// success. Returns false when something is wrong.
117bool Get32u(StreamInterface* stream, const std::uint32_t offset,
118 const tiff_directory::Endian& endian, std::uint32_t* value);
119
120// Retrieves a byte vector of size 'length' from 'stream' beginning at some
121// 'offset' reading the data in chunks of one MiB.
122// If 'error' is not set to kOk the returned value is invalid.
123std::vector<std::uint8_t> GetData(const size_t offset, const size_t length,
124 StreamInterface* stream, Error* error);
125
126// Retrieves the endianness of TIFF compliant data at 'tiff_offset' from
127// 'stream' returning true on success. Returns false when something is wrong.
128bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream,
129 tiff_directory::Endian* endian);
130
131// Retrieves an image from tiff_directory. Return false when something is wrong.
132bool GetImageData(const tiff_directory::TiffDirectory& tiff_directory,
133 StreamInterface* stream, Image* image);
134
135// Retrieves the width and height from the jpeg image returning true on
136// success. Returns false when something is wrong.
137bool GetJpegDimensions(const std::uint32_t jpeg_offset, StreamInterface* stream,
138 std::uint16_t* width, std::uint16_t* height);
139
140// According to Tiff/EP a thumbnail has max 256 pixels per dimension.
141// http://standardsproposals.bsigroup.com/Home/getPDF/567
142bool IsThumbnail(const Image& image,
143 const int max_dimension = kThumbnailMaxDimension);
144
145// Parses through a Tiff IFD and writes all 'desired_tags' to a
146// 'tiff_directory'.
147// Returns false if something with the Tiff data is wrong.
148bool ParseDirectory(const std::uint32_t tiff_offset,
149 const std::uint32_t ifd_offset,
150 const tiff_directory::Endian endian,
151 const TagSet& desired_tags, StreamInterface* stream,
152 tiff_directory::TiffDirectory* tiff_directory,
153 std::uint32_t* next_ifd_offset);
154
155// Returns true if Exif orientation for the image can be obtained. False
156// otherwise.
157bool GetExifOrientation(StreamInterface* stream, const std::uint32_t offset,
158 std::uint32_t* orientation);
159
160// Reads the width and height of the full resolution image. The tag groups are
161// exclusive.
162bool GetFullDimension32(const tiff_directory::TiffDirectory& tiff_directory,
163 std::uint32_t* width, std::uint32_t* height);
164
165// Reads the width and height of the crop information if available.
166// Returns false if an error occurred.
167bool GetFullCropDimension(const tiff_directory::TiffDirectory& tiff_directory,
168 std::uint32_t* width, std::uint32_t* height);
169
170// Enables us to parse through data that complies to the Tiff/EP specification.
171class TiffParser {
172 public:
173 // The caller owns 'stream' and is responsible to keep it alive while the
174 // TiffParser object is used.
175 explicit TiffParser(StreamInterface* stream);
176 TiffParser(StreamInterface* stream, const std::uint32_t offset);
177
178 // Runs over the Tiff IFD, Exif IFD and subIFDs to get the preview image data.
179 // Returns false if something with the Tiff tags is wrong.
180 bool GetPreviewImageData(const TiffContent& tiff_content,
181 PreviewImageData* image_metadata);
182
183 // Returns false if called more that once or something with the Tiff data is
184 // wrong.
185 bool Parse(const TagSet& desired_tags, const std::uint16_t max_number_ifds,
186 TiffContent* tiff_content);
187
188 private:
189 // Disallow copy and assignment.
190 TiffParser(const TiffParser&) = delete;
191 TiffParser& operator=(const TiffParser&) = delete;
192
193 bool ParseIfd(const std::uint32_t ifd_offset, const TagSet& desired_tags,
194 const std::uint16_t max_number_ifds, IfdVector* tiff_directory);
195 bool ParseGpsData(const tiff_directory::TiffDirectory* tiff_ifd,
196 TiffContent* tiff_content);
197
198 StreamInterface* stream_ = nullptr;
199 std::uint32_t tiff_offset_ = 0;
200 tiff_directory::Endian endian_;
201};
202
203} // namespace piex
204
205#endif // PIEX_TIFF_PARSER_H_
206