Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
6 #include "EXIF.h"
8 #include "mozilla/Endian.h"
10 namespace mozilla {
11 namespace image {
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.
19 enum EXIFTag
20 {
21 OrientationTag = 0x112,
22 };
24 // See Section 4.6.2.
25 enum EXIFType
26 {
27 ByteType = 1,
28 ASCIIType = 2,
29 ShortType = 3,
30 LongType = 4,
31 RationalType = 5,
32 UndefinedType = 7,
33 SignedLongType = 9,
34 SignedRational = 10,
35 };
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 /////////////////////////////////////////////////////////////
43 EXIFData
44 EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength)
45 {
46 if (!Initialize(aData, aLength))
47 return EXIFData();
49 if (!ParseEXIFHeader())
50 return EXIFData();
52 uint32_t offsetIFD;
53 if (!ParseTIFFHeader(offsetIFD))
54 return EXIFData();
56 JumpTo(offsetIFD);
58 Orientation orientation;
59 if (!ParseIFD0(orientation))
60 return EXIFData();
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);
65 }
67 /////////////////////////////////////////////////////////
68 // Parse the EXIF header. (Section 4.7.2, Figure 30)
69 /////////////////////////////////////////////////////////
70 bool
71 EXIFParser::ParseEXIFHeader()
72 {
73 return MatchString(EXIFHeader, EXIFHeaderLength);
74 }
76 /////////////////////////////////////////////////////////
77 // Parse the TIFF header. (Section 4.5.2, Table 1)
78 /////////////////////////////////////////////////////////
79 bool
80 EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut)
81 {
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;
87 else
88 return false;
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.)
92 uint32_t ifd0Offset;
93 if (!ReadUInt32(ifd0Offset) || ifd0Offset > 64 * 1024)
94 return false;
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
98 // appropriately.
99 aIFD0OffsetOut = ifd0Offset + EXIFHeaderLength;
100 return true;
101 }
103 /////////////////////////////////////////////////////////
104 // Parse the entries in IFD0. (Section 4.6.2)
105 /////////////////////////////////////////////////////////
106 bool
107 EXIFParser::ParseIFD0(Orientation& aOrientationOut)
108 {
109 uint16_t entryCount;
110 if (!ReadUInt16(entryCount))
111 return false;
113 for (uint16_t entry = 0 ; entry < entryCount ; ++entry) {
114 // Read the fields of the entry.
115 uint16_t tag;
116 if (!ReadUInt16(tag))
117 return false;
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) {
122 Advance(10);
123 continue;
124 }
126 uint16_t type;
127 if (!ReadUInt16(type))
128 return false;
130 uint32_t count;
131 if (!ReadUInt32(count))
132 return false;
134 // We should have an orientation value here; go ahead and parse it.
135 Orientation orientation;
136 if (!ParseOrientation(type, count, aOrientationOut))
137 return false;
139 // Since the orientation is all we care about, we're done.
140 return true;
141 }
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();
146 return true;
147 }
149 bool
150 EXIFParser::ParseOrientation(uint16_t aType, uint32_t aCount, Orientation& aOut)
151 {
152 // Sanity check the type and count.
153 if (aType != ShortType || aCount != 1)
154 return false;
156 uint16_t value;
157 if (!ReadUInt16(value))
158 return false;
160 switch (value) {
161 case 1: aOut = Orientation(Angle::D0, Flip::Unflipped); break;
162 case 2: aOut = Orientation(Angle::D0, Flip::Horizontal); break;
163 case 3: aOut = Orientation(Angle::D180, Flip::Unflipped); break;
164 case 4: aOut = Orientation(Angle::D180, Flip::Horizontal); break;
165 case 5: aOut = Orientation(Angle::D90, Flip::Horizontal); break;
166 case 6: aOut = Orientation(Angle::D90, Flip::Unflipped); break;
167 case 7: aOut = Orientation(Angle::D270, Flip::Horizontal); break;
168 case 8: aOut = Orientation(Angle::D270, Flip::Unflipped); break;
169 default: return false;
170 }
172 // This is a 32-bit field, but the orientation value only occupies the first
173 // 16 bits. We need to advance another 16 bits to consume the entire field.
174 Advance(2);
175 return true;
176 }
178 bool
179 EXIFParser::Initialize(const uint8_t* aData, const uint32_t aLength)
180 {
181 if (aData == nullptr)
182 return false;
184 // An APP1 segment larger than 64k violates the JPEG standard.
185 if (aLength > 64 * 1024)
186 return false;
188 mStart = mCurrent = aData;
189 mLength = mRemainingLength = aLength;
190 mByteOrder = ByteOrder::Unknown;
191 return true;
192 }
194 void
195 EXIFParser::Advance(const uint32_t aDistance)
196 {
197 if (mRemainingLength >= aDistance) {
198 mCurrent += aDistance;
199 mRemainingLength -= aDistance;
200 } else {
201 mCurrent = mStart;
202 mRemainingLength = 0;
203 }
204 }
206 void
207 EXIFParser::JumpTo(const uint32_t aOffset)
208 {
209 if (mLength >= aOffset) {
210 mCurrent = mStart + aOffset;
211 mRemainingLength = mLength - aOffset;
212 } else {
213 mCurrent = mStart;
214 mRemainingLength = 0;
215 }
216 }
218 bool
219 EXIFParser::MatchString(const char* aString, const uint32_t aLength)
220 {
221 if (mRemainingLength < aLength)
222 return false;
224 for (uint32_t i = 0 ; i < aLength ; ++i) {
225 if (mCurrent[i] != aString[i])
226 return false;
227 }
229 Advance(aLength);
230 return true;
231 }
233 bool
234 EXIFParser::MatchUInt16(const uint16_t aValue)
235 {
236 if (mRemainingLength < 2)
237 return false;
239 bool matched;
240 switch (mByteOrder) {
241 case ByteOrder::LittleEndian:
242 matched = LittleEndian::readUint16(mCurrent) == aValue;
243 break;
244 case ByteOrder::BigEndian:
245 matched = BigEndian::readUint16(mCurrent) == aValue;
246 break;
247 default:
248 NS_NOTREACHED("Should know the byte order by now");
249 matched = false;
250 }
252 if (matched)
253 Advance(2);
255 return matched;
256 }
258 bool
259 EXIFParser::ReadUInt16(uint16_t& aValue)
260 {
261 if (mRemainingLength < 2)
262 return false;
264 bool matched = true;
265 switch (mByteOrder) {
266 case ByteOrder::LittleEndian:
267 aValue = LittleEndian::readUint16(mCurrent);
268 break;
269 case ByteOrder::BigEndian:
270 aValue = BigEndian::readUint16(mCurrent);
271 break;
272 default:
273 NS_NOTREACHED("Should know the byte order by now");
274 matched = false;
275 }
277 if (matched)
278 Advance(2);
280 return matched;
281 }
283 bool
284 EXIFParser::ReadUInt32(uint32_t& aValue)
285 {
286 if (mRemainingLength < 4)
287 return false;
289 bool matched = true;
290 switch (mByteOrder) {
291 case ByteOrder::LittleEndian:
292 aValue = LittleEndian::readUint32(mCurrent);
293 break;
294 case ByteOrder::BigEndian:
295 aValue = BigEndian::readUint32(mCurrent);
296 break;
297 default:
298 NS_NOTREACHED("Should know the byte order by now");
299 matched = false;
300 }
302 if (matched)
303 Advance(4);
305 return matched;
306 }
308 } // namespace image
309 } // namespace mozilla