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/EndianUtils.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.
20 OrientationTag
= 0x112,
35 static const char* EXIFHeader
= "Exif\0\0";
36 static const uint32_t EXIFHeaderLength
= 6;
38 /////////////////////////////////////////////////////////////
39 // Parse EXIF data, typically found in a JPEG's APP1 segment.
40 /////////////////////////////////////////////////////////////
41 EXIFData
EXIFParser::ParseEXIF(const uint8_t* aData
, const uint32_t aLength
) {
42 if (!Initialize(aData
, aLength
)) {
46 if (!ParseEXIFHeader()) {
51 if (!ParseTIFFHeader(offsetIFD
)) {
57 Orientation orientation
;
58 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 /////////////////////////////////////////////////////////
70 bool EXIFParser::ParseEXIFHeader() {
71 return MatchString(EXIFHeader
, EXIFHeaderLength
);
74 /////////////////////////////////////////////////////////
75 // Parse the TIFF header. (Section 4.5.2, Table 1)
76 /////////////////////////////////////////////////////////
77 bool EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut
) {
78 // Determine byte order.
79 if (MatchString("MM\0*", 4)) {
80 mByteOrder
= ByteOrder::BigEndian
;
81 } else if (MatchString("II*\0", 4)) {
82 mByteOrder
= ByteOrder::LittleEndian
;
87 // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
88 // is the maximum size of the entry APP1 segment.)
90 if (!ReadUInt32(ifd0Offset
) || ifd0Offset
> 64 * 1024) {
94 // The IFD offset is relative to the beginning of the TIFF header, which
95 // begins after the EXIF header, so we need to increase the offset
97 aIFD0OffsetOut
= ifd0Offset
+ EXIFHeaderLength
;
101 /////////////////////////////////////////////////////////
102 // Parse the entries in IFD0. (Section 4.6.2)
103 /////////////////////////////////////////////////////////
104 bool EXIFParser::ParseIFD0(Orientation
& aOrientationOut
) {
106 if (!ReadUInt16(entryCount
)) {
110 for (uint16_t entry
= 0; entry
< entryCount
; ++entry
) {
111 // Read the fields of the entry.
113 if (!ReadUInt16(tag
)) {
117 // Right now, we only care about orientation, so we immediately skip to the
118 // next entry if we find anything else.
119 if (tag
!= OrientationTag
) {
125 if (!ReadUInt16(type
)) {
130 if (!ReadUInt32(count
)) {
134 // We should have an orientation value here; go ahead and parse it.
135 if (!ParseOrientation(type
, count
, aOrientationOut
)) {
139 // Since the orientation is all we care about, we're done.
143 // We didn't find an orientation field in the IFD. That's OK; we assume the
144 // default orientation in that case.
145 aOrientationOut
= Orientation();
149 bool EXIFParser::ParseOrientation(uint16_t aType
, uint32_t aCount
,
151 // Sanity check the type and count.
152 if (aType
!= ShortType
|| aCount
!= 1) {
157 if (!ReadUInt16(value
)) {
163 aOut
= Orientation(Angle::D0
, Flip::Unflipped
);
166 aOut
= Orientation(Angle::D0
, Flip::Horizontal
);
169 aOut
= Orientation(Angle::D180
, Flip::Unflipped
);
172 aOut
= Orientation(Angle::D180
, Flip::Horizontal
);
175 aOut
= Orientation(Angle::D90
, Flip::Horizontal
);
178 aOut
= Orientation(Angle::D90
, Flip::Unflipped
);
181 aOut
= Orientation(Angle::D270
, Flip::Horizontal
);
184 aOut
= Orientation(Angle::D270
, Flip::Unflipped
);
190 // This is a 32-bit field, but the orientation value only occupies the first
191 // 16 bits. We need to advance another 16 bits to consume the entire field.
196 bool EXIFParser::Initialize(const uint8_t* aData
, const uint32_t aLength
) {
197 if (aData
== nullptr) {
201 // An APP1 segment larger than 64k violates the JPEG standard.
202 if (aLength
> 64 * 1024) {
206 mStart
= mCurrent
= aData
;
207 mLength
= mRemainingLength
= aLength
;
208 mByteOrder
= ByteOrder::Unknown
;
212 void EXIFParser::Advance(const uint32_t aDistance
) {
213 if (mRemainingLength
>= aDistance
) {
214 mCurrent
+= aDistance
;
215 mRemainingLength
-= aDistance
;
218 mRemainingLength
= 0;
222 void EXIFParser::JumpTo(const uint32_t aOffset
) {
223 if (mLength
>= aOffset
) {
224 mCurrent
= mStart
+ aOffset
;
225 mRemainingLength
= mLength
- aOffset
;
228 mRemainingLength
= 0;
232 bool EXIFParser::MatchString(const char* aString
, const uint32_t aLength
) {
233 if (mRemainingLength
< aLength
) {
237 for (uint32_t i
= 0; i
< aLength
; ++i
) {
238 if (mCurrent
[i
] != aString
[i
]) {
247 bool EXIFParser::MatchUInt16(const uint16_t aValue
) {
248 if (mRemainingLength
< 2) {
253 switch (mByteOrder
) {
254 case ByteOrder::LittleEndian
:
255 matched
= LittleEndian::readUint16(mCurrent
) == aValue
;
257 case ByteOrder::BigEndian
:
258 matched
= BigEndian::readUint16(mCurrent
) == aValue
;
261 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
272 bool EXIFParser::ReadUInt16(uint16_t& aValue
) {
273 if (mRemainingLength
< 2) {
278 switch (mByteOrder
) {
279 case ByteOrder::LittleEndian
:
280 aValue
= LittleEndian::readUint16(mCurrent
);
282 case ByteOrder::BigEndian
:
283 aValue
= BigEndian::readUint16(mCurrent
);
286 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
297 bool EXIFParser::ReadUInt32(uint32_t& aValue
) {
298 if (mRemainingLength
< 4) {
303 switch (mByteOrder
) {
304 case ByteOrder::LittleEndian
:
305 aValue
= LittleEndian::readUint32(mCurrent
);
307 case ByteOrder::BigEndian
:
308 aValue
= BigEndian::readUint32(mCurrent
);
311 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
323 } // namespace mozilla