123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "EXIF.h"
- #include "mozilla/EndianUtils.h"
- namespace mozilla {
- namespace image {
- // Section references in this file refer to the EXIF v2.3 standard, also known
- // as CIPA DC-008-Translation-2010.
- // See Section 4.6.4, Table 4.
- // Typesafe enums are intentionally not used here since we're comparing to raw
- // integers produced by parsing.
- enum EXIFTag
- {
- OrientationTag = 0x112,
- };
- // See Section 4.6.2.
- enum EXIFType
- {
- ByteType = 1,
- ASCIIType = 2,
- ShortType = 3,
- LongType = 4,
- RationalType = 5,
- UndefinedType = 7,
- SignedLongType = 9,
- SignedRational = 10,
- };
- static const char* EXIFHeader = "Exif\0\0";
- static const uint32_t EXIFHeaderLength = 6;
- /////////////////////////////////////////////////////////////
- // Parse EXIF data, typically found in a JPEG's APP1 segment.
- /////////////////////////////////////////////////////////////
- EXIFData
- EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength)
- {
- if (!Initialize(aData, aLength)) {
- return EXIFData();
- }
- if (!ParseEXIFHeader()) {
- return EXIFData();
- }
- uint32_t offsetIFD;
- if (!ParseTIFFHeader(offsetIFD)) {
- return EXIFData();
- }
- JumpTo(offsetIFD);
- Orientation orientation;
- if (!ParseIFD0(orientation)) {
- return EXIFData();
- }
- // We only care about orientation at this point, so we don't bother with the
- // other IFDs. If we got this far we're done.
- return EXIFData(orientation);
- }
- /////////////////////////////////////////////////////////
- // Parse the EXIF header. (Section 4.7.2, Figure 30)
- /////////////////////////////////////////////////////////
- bool
- EXIFParser::ParseEXIFHeader()
- {
- return MatchString(EXIFHeader, EXIFHeaderLength);
- }
- /////////////////////////////////////////////////////////
- // Parse the TIFF header. (Section 4.5.2, Table 1)
- /////////////////////////////////////////////////////////
- bool
- EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut)
- {
- // Determine byte order.
- if (MatchString("MM\0*", 4)) {
- mByteOrder = ByteOrder::BigEndian;
- } else if (MatchString("II*\0", 4)) {
- mByteOrder = ByteOrder::LittleEndian;
- } else {
- return false;
- }
- // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
- // is the maximum size of the entry APP1 segment.)
- uint32_t ifd0Offset;
- if (!ReadUInt32(ifd0Offset) || ifd0Offset > 64 * 1024) {
- return false;
- }
- // The IFD offset is relative to the beginning of the TIFF header, which
- // begins after the EXIF header, so we need to increase the offset
- // appropriately.
- aIFD0OffsetOut = ifd0Offset + EXIFHeaderLength;
- return true;
- }
- /////////////////////////////////////////////////////////
- // Parse the entries in IFD0. (Section 4.6.2)
- /////////////////////////////////////////////////////////
- bool
- EXIFParser::ParseIFD0(Orientation& aOrientationOut)
- {
- uint16_t entryCount;
- if (!ReadUInt16(entryCount)) {
- return false;
- }
- for (uint16_t entry = 0 ; entry < entryCount ; ++entry) {
- // Read the fields of the entry.
- uint16_t tag;
- if (!ReadUInt16(tag)) {
- return false;
- }
- // Right now, we only care about orientation, so we immediately skip to the
- // next entry if we find anything else.
- if (tag != OrientationTag) {
- Advance(10);
- continue;
- }
- uint16_t type;
- if (!ReadUInt16(type)) {
- return false;
- }
- uint32_t count;
- if (!ReadUInt32(count)) {
- return false;
- }
- // We should have an orientation value here; go ahead and parse it.
- if (!ParseOrientation(type, count, aOrientationOut)) {
- return false;
- }
- // Since the orientation is all we care about, we're done.
- return true;
- }
- // We didn't find an orientation field in the IFD. That's OK; we assume the
- // default orientation in that case.
- aOrientationOut = Orientation();
- return true;
- }
- bool
- EXIFParser::ParseOrientation(uint16_t aType, uint32_t aCount, Orientation& aOut)
- {
- // Sanity check the type and count.
- if (aType != ShortType || aCount != 1) {
- return false;
- }
- uint16_t value;
- if (!ReadUInt16(value)) {
- return false;
- }
- switch (value) {
- case 1: aOut = Orientation(Angle::D0, Flip::Unflipped); break;
- case 2: aOut = Orientation(Angle::D0, Flip::Horizontal); break;
- case 3: aOut = Orientation(Angle::D180, Flip::Unflipped); break;
- case 4: aOut = Orientation(Angle::D180, Flip::Horizontal); break;
- case 5: aOut = Orientation(Angle::D90, Flip::Horizontal); break;
- case 6: aOut = Orientation(Angle::D90, Flip::Unflipped); break;
- case 7: aOut = Orientation(Angle::D270, Flip::Horizontal); break;
- case 8: aOut = Orientation(Angle::D270, Flip::Unflipped); break;
- default: return false;
- }
- // This is a 32-bit field, but the orientation value only occupies the first
- // 16 bits. We need to advance another 16 bits to consume the entire field.
- Advance(2);
- return true;
- }
- bool
- EXIFParser::Initialize(const uint8_t* aData, const uint32_t aLength)
- {
- if (aData == nullptr) {
- return false;
- }
- // An APP1 segment larger than 64k violates the JPEG standard.
- if (aLength > 64 * 1024) {
- return false;
- }
- mStart = mCurrent = aData;
- mLength = mRemainingLength = aLength;
- mByteOrder = ByteOrder::Unknown;
- return true;
- }
- void
- EXIFParser::Advance(const uint32_t aDistance)
- {
- if (mRemainingLength >= aDistance) {
- mCurrent += aDistance;
- mRemainingLength -= aDistance;
- } else {
- mCurrent = mStart;
- mRemainingLength = 0;
- }
- }
- void
- EXIFParser::JumpTo(const uint32_t aOffset)
- {
- if (mLength >= aOffset) {
- mCurrent = mStart + aOffset;
- mRemainingLength = mLength - aOffset;
- } else {
- mCurrent = mStart;
- mRemainingLength = 0;
- }
- }
- bool
- EXIFParser::MatchString(const char* aString, const uint32_t aLength)
- {
- if (mRemainingLength < aLength) {
- return false;
- }
- for (uint32_t i = 0 ; i < aLength ; ++i) {
- if (mCurrent[i] != aString[i]) {
- return false;
- }
- }
- Advance(aLength);
- return true;
- }
- bool
- EXIFParser::MatchUInt16(const uint16_t aValue)
- {
- if (mRemainingLength < 2) {
- return false;
- }
- bool matched;
- switch (mByteOrder) {
- case ByteOrder::LittleEndian:
- matched = LittleEndian::readUint16(mCurrent) == aValue;
- break;
- case ByteOrder::BigEndian:
- matched = BigEndian::readUint16(mCurrent) == aValue;
- break;
- default:
- NS_NOTREACHED("Should know the byte order by now");
- matched = false;
- }
- if (matched) {
- Advance(2);
- }
- return matched;
- }
- bool
- EXIFParser::ReadUInt16(uint16_t& aValue)
- {
- if (mRemainingLength < 2) {
- return false;
- }
- bool matched = true;
- switch (mByteOrder) {
- case ByteOrder::LittleEndian:
- aValue = LittleEndian::readUint16(mCurrent);
- break;
- case ByteOrder::BigEndian:
- aValue = BigEndian::readUint16(mCurrent);
- break;
- default:
- NS_NOTREACHED("Should know the byte order by now");
- matched = false;
- }
- if (matched) {
- Advance(2);
- }
- return matched;
- }
- bool
- EXIFParser::ReadUInt32(uint32_t& aValue)
- {
- if (mRemainingLength < 4) {
- return false;
- }
- bool matched = true;
- switch (mByteOrder) {
- case ByteOrder::LittleEndian:
- aValue = LittleEndian::readUint32(mCurrent);
- break;
- case ByteOrder::BigEndian:
- aValue = BigEndian::readUint32(mCurrent);
- break;
- default:
- NS_NOTREACHED("Should know the byte order by now");
- matched = false;
- }
- if (matched) {
- Advance(4);
- }
- return matched;
- }
- } // namespace image
- } // namespace mozilla
|