1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/Endian.h"
13 // Section references in this file refer to the EXIF v2.3 standard, also known
14 // as CIPA DC-008-Translation-2010.
16 // See Section 4.6.4, Table 4.
17 // Typesafe enums are intentionally not used here since we're comparing to raw
18 // integers produced by parsing.
21 OrientationTag
= 0x112,
37 static const char* EXIFHeader
= "Exif\0\0";
38 static const uint32_t EXIFHeaderLength
= 6;
40 /////////////////////////////////////////////////////////////
41 // Parse EXIF data, typically found in a JPEG's APP1 segment.
42 /////////////////////////////////////////////////////////////
44 EXIFParser::ParseEXIF(const uint8_t* aData
, const uint32_t aLength
)
46 if (!Initialize(aData
, aLength
))
49 if (!ParseEXIFHeader())
53 if (!ParseTIFFHeader(offsetIFD
))
58 Orientation orientation
;
59 if (!ParseIFD0(orientation
))
62 // We only care about orientation at this point, so we don't bother with the
63 // other IFDs. If we got this far we're done.
64 return EXIFData(orientation
);
67 /////////////////////////////////////////////////////////
68 // Parse the EXIF header. (Section 4.7.2, Figure 30)
69 /////////////////////////////////////////////////////////
71 EXIFParser::ParseEXIFHeader()
73 return MatchString(EXIFHeader
, EXIFHeaderLength
);
76 /////////////////////////////////////////////////////////
77 // Parse the TIFF header. (Section 4.5.2, Table 1)
78 /////////////////////////////////////////////////////////
80 EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut
)
82 // Determine byte order.
83 if (MatchString("MM\0*", 4))
84 mByteOrder
= ByteOrder::BigEndian
;
85 else if (MatchString("II*\0", 4))
86 mByteOrder
= ByteOrder::LittleEndian
;
90 // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
91 // is the maximum size of the entry APP1 segment.)
93 if (!ReadUInt32(ifd0Offset
) || ifd0Offset
> 64 * 1024)
96 // The IFD offset is relative to the beginning of the TIFF header, which
97 // begins after the EXIF header, so we need to increase the offset
99 aIFD0OffsetOut
= ifd0Offset
+ EXIFHeaderLength
;
103 /////////////////////////////////////////////////////////
104 // Parse the entries in IFD0. (Section 4.6.2)
105 /////////////////////////////////////////////////////////
107 EXIFParser::ParseIFD0(Orientation
& aOrientationOut
)
110 if (!ReadUInt16(entryCount
))
113 for (uint16_t entry
= 0 ; entry
< entryCount
; ++entry
) {
114 // Read the fields of the entry.
116 if (!ReadUInt16(tag
))
119 // Right now, we only care about orientation, so we immediately skip to the
120 // next entry if we find anything else.
121 if (tag
!= OrientationTag
) {
127 if (!ReadUInt16(type
))
131 if (!ReadUInt32(count
))
134 // We should have an orientation value here; go ahead and parse it.
135 if (!ParseOrientation(type
, count
, aOrientationOut
))
138 // Since the orientation is all we care about, we're done.
142 // We didn't find an orientation field in the IFD. That's OK; we assume the
143 // default orientation in that case.
144 aOrientationOut
= Orientation();
149 EXIFParser::ParseOrientation(uint16_t aType
, uint32_t aCount
, Orientation
& aOut
)
151 // Sanity check the type and count.
152 if (aType
!= ShortType
|| aCount
!= 1)
156 if (!ReadUInt16(value
))
160 case 1: aOut
= Orientation(Angle::D0
, Flip::Unflipped
); break;
161 case 2: aOut
= Orientation(Angle::D0
, Flip::Horizontal
); break;
162 case 3: aOut
= Orientation(Angle::D180
, Flip::Unflipped
); break;
163 case 4: aOut
= Orientation(Angle::D180
, Flip::Horizontal
); break;
164 case 5: aOut
= Orientation(Angle::D90
, Flip::Horizontal
); break;
165 case 6: aOut
= Orientation(Angle::D90
, Flip::Unflipped
); break;
166 case 7: aOut
= Orientation(Angle::D270
, Flip::Horizontal
); break;
167 case 8: aOut
= Orientation(Angle::D270
, Flip::Unflipped
); break;
168 default: return false;
171 // This is a 32-bit field, but the orientation value only occupies the first
172 // 16 bits. We need to advance another 16 bits to consume the entire field.
178 EXIFParser::Initialize(const uint8_t* aData
, const uint32_t aLength
)
180 if (aData
== nullptr)
183 // An APP1 segment larger than 64k violates the JPEG standard.
184 if (aLength
> 64 * 1024)
187 mStart
= mCurrent
= aData
;
188 mLength
= mRemainingLength
= aLength
;
189 mByteOrder
= ByteOrder::Unknown
;
194 EXIFParser::Advance(const uint32_t aDistance
)
196 if (mRemainingLength
>= aDistance
) {
197 mCurrent
+= aDistance
;
198 mRemainingLength
-= aDistance
;
201 mRemainingLength
= 0;
206 EXIFParser::JumpTo(const uint32_t aOffset
)
208 if (mLength
>= aOffset
) {
209 mCurrent
= mStart
+ aOffset
;
210 mRemainingLength
= mLength
- aOffset
;
213 mRemainingLength
= 0;
218 EXIFParser::MatchString(const char* aString
, const uint32_t aLength
)
220 if (mRemainingLength
< aLength
)
223 for (uint32_t i
= 0 ; i
< aLength
; ++i
) {
224 if (mCurrent
[i
] != aString
[i
])
233 EXIFParser::MatchUInt16(const uint16_t aValue
)
235 if (mRemainingLength
< 2)
239 switch (mByteOrder
) {
240 case ByteOrder::LittleEndian
:
241 matched
= LittleEndian::readUint16(mCurrent
) == aValue
;
243 case ByteOrder::BigEndian
:
244 matched
= BigEndian::readUint16(mCurrent
) == aValue
;
247 NS_NOTREACHED("Should know the byte order by now");
258 EXIFParser::ReadUInt16(uint16_t& aValue
)
260 if (mRemainingLength
< 2)
264 switch (mByteOrder
) {
265 case ByteOrder::LittleEndian
:
266 aValue
= LittleEndian::readUint16(mCurrent
);
268 case ByteOrder::BigEndian
:
269 aValue
= BigEndian::readUint16(mCurrent
);
272 NS_NOTREACHED("Should know the byte order by now");
283 EXIFParser::ReadUInt32(uint32_t& aValue
)
285 if (mRemainingLength
< 4)
289 switch (mByteOrder
) {
290 case ByteOrder::LittleEndian
:
291 aValue
= LittleEndian::readUint32(mCurrent
);
293 case ByteOrder::BigEndian
:
294 aValue
= BigEndian::readUint32(mCurrent
);
297 NS_NOTREACHED("Should know the byte order by now");
308 } // namespace mozilla