| // Copyright 2015 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| #ifndef PIEX_TIFF_PARSER_H_ |
| #define PIEX_TIFF_PARSER_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <set> |
| #include <vector> |
| |
| #include "src/piex_types.h" |
| #include "src/tiff_directory/tiff_directory.h" |
| |
| namespace piex { |
| |
| // Specifies the maximum number of pixels for thumbnails in each direction. |
| const int kThumbnailMaxDimension = 512; |
| |
| // Specifies all tags that might be of interest to get the preview data. |
| enum GpsTags { |
| kGpsTagLatitudeRef = 1, |
| kGpsTagLatitude = 2, |
| kGpsTagLongitudeRef = 3, |
| kGpsTagLongitude = 4, |
| kGpsTagAltitudeRef = 5, |
| kGpsTagAltitude = 6, |
| kGpsTagTimeStamp = 7, |
| kGpsTagDateStamp = 29, |
| }; |
| |
| enum TiffTags { |
| kExifTagColorSpace = 0xA001, |
| kExifTagDateTimeOriginal = 0x9003, |
| kExifTagDefaultCropSize = 0xC620, |
| kExifTagExposureTime = 0x829a, |
| kExifTagFnumber = 0x829d, |
| kExifTagFocalLength = 0x920A, |
| kExifTagGps = 0x8825, |
| kExifTagHeight = 0xA003, |
| kExifTagIsoSpeed = 0x8827, |
| kExifTagMakernotes = 0x927C, |
| kExifTagWidth = 0xA002, |
| kOlymTagAspectFrame = 0x1113, |
| kOlymTagCameraSettings = 0x2020, |
| kOlymTagRawProcessing = 0x2040, |
| kPanaTagBottomBorder = 0x006, |
| kPanaTagIso = 0x0017, |
| kPanaTagJpegImage = 0x002E, |
| kPanaTagLeftBorder = 0x0005, |
| kPanaTagRightBorder = 0x007, |
| kPanaTagTopBorder = 0x0004, |
| kPentaxTagColorSpace = 0x0037, |
| kTiffTagArtist = 0x013B, |
| kTiffTagBitsPerSample = 0x0102, |
| kTiffTagCfaPatternDim = 0x828D, |
| kTiffTagCompression = 0x0103, |
| kTiffTagDateTime = 0x0132, |
| kTiffTagExifIfd = 0x8769, |
| kTiffTagImageDescription = 0x010E, |
| kTiffTagImageLength = 0x0101, |
| kTiffTagImageWidth = 0x0100, |
| kTiffTagJpegByteCount = 0x0202, |
| kTiffTagJpegOffset = 0x0201, |
| kTiffTagMake = 0x010F, |
| kTiffTagModel = 0x0110, |
| kTiffTagOrientation = 0x0112, |
| kTiffTagPhotometric = 0x0106, |
| kTiffTagPlanarConfig = 0x011C, |
| kTiffTagResolutionUnit = 0x0128, |
| kTiffTagRowsPerStrip = 0x0116, |
| kTiffTagSamplesPerPixel = 0x0115, |
| kTiffTagSoftware = 0x0131, |
| kTiffTagStripByteCounts = 0x0117, |
| kTiffTagStripOffsets = 0x0111, |
| kTiffTagSubFileType = 0x00FE, |
| kTiffTagSubIfd = 0x014A, |
| kTiffTagTileByteCounts = 0x0145, |
| kTiffTagTileLength = 0x0143, |
| kTiffTagTileOffsets = 0x0144, |
| kTiffTagTileWidth = 0x0142, |
| kTiffTagXresolution = 0x011A, |
| kTiffTagYresolution = 0x011B, |
| }; |
| |
| typedef std::set<tiff_directory::TiffDirectory::Tag> TagSet; |
| typedef std::vector<tiff_directory::TiffDirectory> IfdVector; |
| |
| struct TiffContent { |
| IfdVector tiff_directory; |
| std::unique_ptr<tiff_directory::TiffDirectory> exif_directory; |
| std::unique_ptr<tiff_directory::TiffDirectory> gps_directory; |
| }; |
| |
| // Reads 2 bytes, an unsigned 16bit from 'stream' at a certain 'offset'. The |
| // bytes get swapped according to the desired endianness returning true on |
| // success. Returns false when something is wrong. |
| bool Get16u(StreamInterface* stream, const std::uint32_t offset, |
| const tiff_directory::Endian& endian, std::uint16_t* value); |
| |
| // Reads 4 bytes, an unsigned 32bit 'value' from 'stream' at a certain 'offset'. |
| // The bytes get swapped according to the desired endianness returning true on |
| // success. Returns false when something is wrong. |
| bool Get32u(StreamInterface* stream, const std::uint32_t offset, |
| const tiff_directory::Endian& endian, std::uint32_t* value); |
| |
| // Retrieves a byte vector of size 'length' from 'stream' beginning at some |
| // 'offset' reading the data in chunks of one MiB. |
| // If 'error' is not set to kOk the returned value is invalid. |
| std::vector<std::uint8_t> GetData(const size_t offset, const size_t length, |
| StreamInterface* stream, Error* error); |
| |
| // Retrieves the endianness of TIFF compliant data at 'tiff_offset' from |
| // 'stream' returning true on success. Returns false when something is wrong. |
| bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream, |
| tiff_directory::Endian* endian); |
| |
| // Retrieves an image from tiff_directory. Return false when something is wrong. |
| bool GetImageData(const tiff_directory::TiffDirectory& tiff_directory, |
| StreamInterface* stream, Image* image); |
| |
| // Retrieves the width and height from the jpeg image returning true on |
| // success. Returns false when something is wrong. |
| bool GetJpegDimensions(const std::uint32_t jpeg_offset, StreamInterface* stream, |
| std::uint16_t* width, std::uint16_t* height); |
| |
| // According to Tiff/EP a thumbnail has max 256 pixels per dimension. |
| // http://standardsproposals.bsigroup.com/Home/getPDF/567 |
| bool IsThumbnail(const Image& image, |
| const int max_dimension = kThumbnailMaxDimension); |
| |
| // Parses through a Tiff IFD and writes all 'desired_tags' to a |
| // 'tiff_directory'. |
| // Returns false if something with the Tiff data is wrong. |
| bool ParseDirectory(const std::uint32_t tiff_offset, |
| const std::uint32_t ifd_offset, |
| const tiff_directory::Endian endian, |
| const TagSet& desired_tags, StreamInterface* stream, |
| tiff_directory::TiffDirectory* tiff_directory, |
| std::uint32_t* next_ifd_offset); |
| |
| // Returns true if Exif orientation for the image can be obtained. False |
| // otherwise. |
| bool GetExifOrientation(StreamInterface* stream, const std::uint32_t offset, |
| std::uint32_t* orientation); |
| |
| // Reads the width and height of the full resolution image. The tag groups are |
| // exclusive. |
| bool GetFullDimension32(const tiff_directory::TiffDirectory& tiff_directory, |
| std::uint32_t* width, std::uint32_t* height); |
| |
| // Reads the width and height of the crop information if available. |
| // Returns false if an error occurred. |
| bool GetFullCropDimension(const tiff_directory::TiffDirectory& tiff_directory, |
| std::uint32_t* width, std::uint32_t* height); |
| |
| // Enables us to parse through data that complies to the Tiff/EP specification. |
| class TiffParser { |
| public: |
| // The caller owns 'stream' and is responsible to keep it alive while the |
| // TiffParser object is used. |
| explicit TiffParser(StreamInterface* stream); |
| TiffParser(StreamInterface* stream, const std::uint32_t offset); |
| |
| // Runs over the Tiff IFD, Exif IFD and subIFDs to get the preview image data. |
| // Returns false if something with the Tiff tags is wrong. |
| bool GetPreviewImageData(const TiffContent& tiff_content, |
| PreviewImageData* image_metadata); |
| |
| // Returns false if called more that once or something with the Tiff data is |
| // wrong. |
| bool Parse(const TagSet& desired_tags, const std::uint16_t max_number_ifds, |
| TiffContent* tiff_content); |
| |
| private: |
| // Disallow copy and assignment. |
| TiffParser(const TiffParser&) = delete; |
| TiffParser& operator=(const TiffParser&) = delete; |
| |
| bool ParseIfd(const std::uint32_t ifd_offset, const TagSet& desired_tags, |
| const std::uint16_t max_number_ifds, IfdVector* tiff_directory); |
| bool ParseGpsData(const tiff_directory::TiffDirectory* tiff_ifd, |
| TiffContent* tiff_content); |
| |
| StreamInterface* stream_ = nullptr; |
| std::uint32_t tiff_offset_ = 0; |
| tiff_directory::Endian endian_; |
| }; |
| |
| } // namespace piex |
| |
| #endif // PIEX_TIFF_PARSER_H_ |