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/. */
5
6 #include "EXIF.h"
7
8 #include "mozilla/EndianUtils.h"
9 #include "mozilla/StaticPrefs_image.h"
10
11 namespace mozilla::image {
12
13 // Section references in this file refer to the EXIF v2.3 standard, also known
14 // as CIPA DC-008-Translation-2010.
15
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 class EXIFTag : uint16_t {
20 Orientation = 0x112,
21 XResolution = 0x11a,
22 YResolution = 0x11b,
23 PixelXDimension = 0xa002,
24 PixelYDimension = 0xa003,
25 ResolutionUnit = 0x128,
26 IFDPointer = 0x8769,
27 };
28
29 // See Section 4.6.2.
30 enum EXIFType {
31 ByteType = 1,
32 ASCIIType = 2,
33 ShortType = 3,
34 LongType = 4,
35 RationalType = 5,
36 UndefinedType = 7,
37 SignedLongType = 9,
38 SignedRational = 10,
39 };
40
41 static const char* EXIFHeader = "Exif\0\0";
42 static const uint32_t EXIFHeaderLength = 6;
43 static const uint32_t TIFFHeaderStart = EXIFHeaderLength;
44
45 struct ParsedEXIFData {
46 Orientation orientation;
47 Maybe<float> resolutionX;
48 Maybe<float> resolutionY;
49 Maybe<uint32_t> pixelXDimension;
50 Maybe<uint32_t> pixelYDimension;
51 Maybe<ResolutionUnit> resolutionUnit;
52 };
53
ToDppx(float aResolution,ResolutionUnit aUnit)54 static float ToDppx(float aResolution, ResolutionUnit aUnit) {
55 constexpr float kPointsPerInch = 72.0f;
56 constexpr float kPointsPerCm = 1.0f / 2.54f;
57 switch (aUnit) {
58 case ResolutionUnit::Dpi:
59 return aResolution / kPointsPerInch;
60 case ResolutionUnit::Dpcm:
61 return aResolution / kPointsPerCm;
62 }
63 MOZ_CRASH("Unknown resolution unit?");
64 }
65
ResolutionFromParsedData(const ParsedEXIFData & aData,const gfx::IntSize & aRealImageSize)66 static Resolution ResolutionFromParsedData(const ParsedEXIFData& aData,
67 const gfx::IntSize& aRealImageSize) {
68 if (!aData.resolutionUnit || !aData.resolutionX || !aData.resolutionY) {
69 return {};
70 }
71
72 Resolution resolution{ToDppx(*aData.resolutionX, *aData.resolutionUnit),
73 ToDppx(*aData.resolutionY, *aData.resolutionUnit)};
74
75 if (StaticPrefs::image_exif_density_correction_sanity_check_enabled()) {
76 if (!aData.pixelXDimension || !aData.pixelYDimension) {
77 return {};
78 }
79
80 const gfx::IntSize exifSize(*aData.pixelXDimension, *aData.pixelYDimension);
81
82 gfx::IntSize scaledSize = aRealImageSize;
83 resolution.ApplyTo(scaledSize.width, scaledSize.height);
84
85 if (exifSize != scaledSize) {
86 return {};
87 }
88 }
89
90 return resolution;
91 }
92
93 /////////////////////////////////////////////////////////////
94 // Parse EXIF data, typically found in a JPEG's APP1 segment.
95 /////////////////////////////////////////////////////////////
ParseEXIF(const uint8_t * aData,const uint32_t aLength,const gfx::IntSize & aRealImageSize)96 EXIFData EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength,
97 const gfx::IntSize& aRealImageSize) {
98 if (!Initialize(aData, aLength)) {
99 return EXIFData();
100 }
101
102 if (!ParseEXIFHeader()) {
103 return EXIFData();
104 }
105
106 uint32_t offsetIFD;
107 if (!ParseTIFFHeader(offsetIFD)) {
108 return EXIFData();
109 }
110
111 JumpTo(offsetIFD);
112
113 ParsedEXIFData data;
114 ParseIFD(data);
115
116 return EXIFData{data.orientation,
117 ResolutionFromParsedData(data, aRealImageSize)};
118 }
119
120 /////////////////////////////////////////////////////////
121 // Parse the EXIF header. (Section 4.7.2, Figure 30)
122 /////////////////////////////////////////////////////////
ParseEXIFHeader()123 bool EXIFParser::ParseEXIFHeader() {
124 return MatchString(EXIFHeader, EXIFHeaderLength);
125 }
126
127 /////////////////////////////////////////////////////////
128 // Parse the TIFF header. (Section 4.5.2, Table 1)
129 /////////////////////////////////////////////////////////
ParseTIFFHeader(uint32_t & aIFD0OffsetOut)130 bool EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut) {
131 // Determine byte order.
132 if (MatchString("MM\0*", 4)) {
133 mByteOrder = ByteOrder::BigEndian;
134 } else if (MatchString("II*\0", 4)) {
135 mByteOrder = ByteOrder::LittleEndian;
136 } else {
137 return false;
138 }
139
140 // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
141 // is the maximum size of the entry APP1 segment.)
142 uint32_t ifd0Offset;
143 if (!ReadUInt32(ifd0Offset) || ifd0Offset > 64 * 1024) {
144 return false;
145 }
146
147 // The IFD offset is relative to the beginning of the TIFF header, which
148 // begins after the EXIF header, so we need to increase the offset
149 // appropriately.
150 aIFD0OffsetOut = ifd0Offset + TIFFHeaderStart;
151 return true;
152 }
153
154 // An arbitrary limit on the amount of pointers that we'll chase, to prevent bad
155 // inputs getting us stuck.
156 constexpr uint32_t kMaxEXIFDepth = 16;
157
158 /////////////////////////////////////////////////////////
159 // Parse the entries in IFD0. (Section 4.6.2)
160 /////////////////////////////////////////////////////////
ParseIFD(ParsedEXIFData & aData,uint32_t aDepth)161 void EXIFParser::ParseIFD(ParsedEXIFData& aData, uint32_t aDepth) {
162 if (NS_WARN_IF(aDepth > kMaxEXIFDepth)) {
163 return;
164 }
165
166 uint16_t entryCount;
167 if (!ReadUInt16(entryCount)) {
168 return;
169 }
170
171 for (uint16_t entry = 0; entry < entryCount; ++entry) {
172 // Read the fields of the 12-byte entry.
173 uint16_t tag;
174 if (!ReadUInt16(tag)) {
175 return;
176 }
177
178 uint16_t type;
179 if (!ReadUInt16(type)) {
180 return;
181 }
182
183 uint32_t count;
184 if (!ReadUInt32(count)) {
185 return;
186 }
187
188 switch (EXIFTag(tag)) {
189 case EXIFTag::Orientation:
190 // We should have an orientation value here; go ahead and parse it.
191 if (!ParseOrientation(type, count, aData.orientation)) {
192 return;
193 }
194 break;
195 case EXIFTag::ResolutionUnit:
196 if (!ParseResolutionUnit(type, count, aData.resolutionUnit)) {
197 return;
198 }
199 break;
200 case EXIFTag::XResolution:
201 if (!ParseResolution(type, count, aData.resolutionX)) {
202 return;
203 }
204 break;
205 case EXIFTag::YResolution:
206 if (!ParseResolution(type, count, aData.resolutionY)) {
207 return;
208 }
209 break;
210 case EXIFTag::PixelXDimension:
211 if (!ParseDimension(type, count, aData.pixelXDimension)) {
212 return;
213 }
214 break;
215 case EXIFTag::PixelYDimension:
216 if (!ParseDimension(type, count, aData.pixelYDimension)) {
217 return;
218 }
219 break;
220 case EXIFTag::IFDPointer: {
221 uint32_t offset;
222 if (!ReadUInt32(offset)) {
223 return;
224 }
225
226 ScopedJump jump(*this, offset + TIFFHeaderStart);
227 ParseIFD(aData, aDepth + 1);
228 break;
229 }
230
231 default:
232 Advance(4);
233 break;
234 }
235 }
236 }
237
ReadRational(float & aOut)238 bool EXIFParser::ReadRational(float& aOut) {
239 // Values larger than 4 bytes (like rationals) are specified as an offset into
240 // the TIFF header.
241 uint32_t valueOffset;
242 if (!ReadUInt32(valueOffset)) {
243 return false;
244 }
245 ScopedJump jumpToHeader(*this, valueOffset + TIFFHeaderStart);
246 uint32_t numerator;
247 if (!ReadUInt32(numerator)) {
248 return false;
249 }
250 uint32_t denominator;
251 if (!ReadUInt32(denominator)) {
252 return false;
253 }
254 if (denominator == 0) {
255 return false;
256 }
257 aOut = float(numerator) / float(denominator);
258 return true;
259 }
260
ParseResolution(uint16_t aType,uint32_t aCount,Maybe<float> & aOut)261 bool EXIFParser::ParseResolution(uint16_t aType, uint32_t aCount,
262 Maybe<float>& aOut) {
263 if (!StaticPrefs::image_exif_density_correction_enabled()) {
264 Advance(4);
265 return true;
266 }
267 if (aType != RationalType || aCount != 1) {
268 return false;
269 }
270 float value;
271 if (!ReadRational(value)) {
272 return false;
273 }
274 if (value == 0.0f) {
275 return false;
276 }
277 aOut = Some(value);
278 return true;
279 }
280
ParseDimension(uint16_t aType,uint32_t aCount,Maybe<uint32_t> & aOut)281 bool EXIFParser::ParseDimension(uint16_t aType, uint32_t aCount,
282 Maybe<uint32_t>& aOut) {
283 if (!StaticPrefs::image_exif_density_correction_enabled()) {
284 Advance(4);
285 return true;
286 }
287
288 if (aCount != 1) {
289 return false;
290 }
291
292 switch (aType) {
293 case ShortType: {
294 uint16_t value;
295 if (!ReadUInt16(value)) {
296 return false;
297 }
298 aOut = Some(value);
299 Advance(2);
300 break;
301 }
302 case LongType: {
303 uint32_t value;
304 if (!ReadUInt32(value)) {
305 return false;
306 }
307 aOut = Some(value);
308 break;
309 }
310 default:
311 return false;
312 }
313 return true;
314 }
315
ParseResolutionUnit(uint16_t aType,uint32_t aCount,Maybe<ResolutionUnit> & aOut)316 bool EXIFParser::ParseResolutionUnit(uint16_t aType, uint32_t aCount,
317 Maybe<ResolutionUnit>& aOut) {
318 if (!StaticPrefs::image_exif_density_correction_enabled()) {
319 Advance(4);
320 return true;
321 }
322 if (aType != ShortType || aCount != 1) {
323 return false;
324 }
325 uint16_t value;
326 if (!ReadUInt16(value)) {
327 return false;
328 }
329 switch (value) {
330 case 2:
331 aOut = Some(ResolutionUnit::Dpi);
332 break;
333 case 3:
334 aOut = Some(ResolutionUnit::Dpcm);
335 break;
336 default:
337 return false;
338 }
339
340 // This is a 32-bit field, but the unit value only occupies the first 16 bits.
341 // We need to advance another 16 bits to consume the entire field.
342 Advance(2);
343 return true;
344 }
345
ParseOrientation(uint16_t aType,uint32_t aCount,Orientation & aOut)346 bool EXIFParser::ParseOrientation(uint16_t aType, uint32_t aCount,
347 Orientation& aOut) {
348 // Sanity check the type and count.
349 if (aType != ShortType || aCount != 1) {
350 return false;
351 }
352
353 uint16_t value;
354 if (!ReadUInt16(value)) {
355 return false;
356 }
357
358 switch (value) {
359 case 1:
360 aOut = Orientation(Angle::D0, Flip::Unflipped);
361 break;
362 case 2:
363 aOut = Orientation(Angle::D0, Flip::Horizontal);
364 break;
365 case 3:
366 aOut = Orientation(Angle::D180, Flip::Unflipped);
367 break;
368 case 4:
369 aOut = Orientation(Angle::D180, Flip::Horizontal);
370 break;
371 case 5:
372 aOut = Orientation(Angle::D90, Flip::Horizontal);
373 break;
374 case 6:
375 aOut = Orientation(Angle::D90, Flip::Unflipped);
376 break;
377 case 7:
378 aOut = Orientation(Angle::D270, Flip::Horizontal);
379 break;
380 case 8:
381 aOut = Orientation(Angle::D270, Flip::Unflipped);
382 break;
383 default:
384 return false;
385 }
386
387 // This is a 32-bit field, but the orientation value only occupies the first
388 // 16 bits. We need to advance another 16 bits to consume the entire field.
389 Advance(2);
390 return true;
391 }
392
Initialize(const uint8_t * aData,const uint32_t aLength)393 bool EXIFParser::Initialize(const uint8_t* aData, const uint32_t aLength) {
394 if (aData == nullptr) {
395 return false;
396 }
397
398 // An APP1 segment larger than 64k violates the JPEG standard.
399 if (aLength > 64 * 1024) {
400 return false;
401 }
402
403 mStart = mCurrent = aData;
404 mLength = mRemainingLength = aLength;
405 mByteOrder = ByteOrder::Unknown;
406 return true;
407 }
408
Advance(const uint32_t aDistance)409 void EXIFParser::Advance(const uint32_t aDistance) {
410 if (mRemainingLength >= aDistance) {
411 mCurrent += aDistance;
412 mRemainingLength -= aDistance;
413 } else {
414 mCurrent = mStart;
415 mRemainingLength = 0;
416 }
417 }
418
JumpTo(const uint32_t aOffset)419 void EXIFParser::JumpTo(const uint32_t aOffset) {
420 if (mLength >= aOffset) {
421 mCurrent = mStart + aOffset;
422 mRemainingLength = mLength - aOffset;
423 } else {
424 mCurrent = mStart;
425 mRemainingLength = 0;
426 }
427 }
428
MatchString(const char * aString,const uint32_t aLength)429 bool EXIFParser::MatchString(const char* aString, const uint32_t aLength) {
430 if (mRemainingLength < aLength) {
431 return false;
432 }
433
434 for (uint32_t i = 0; i < aLength; ++i) {
435 if (mCurrent[i] != aString[i]) {
436 return false;
437 }
438 }
439
440 Advance(aLength);
441 return true;
442 }
443
MatchUInt16(const uint16_t aValue)444 bool EXIFParser::MatchUInt16(const uint16_t aValue) {
445 if (mRemainingLength < 2) {
446 return false;
447 }
448
449 bool matched;
450 switch (mByteOrder) {
451 case ByteOrder::LittleEndian:
452 matched = LittleEndian::readUint16(mCurrent) == aValue;
453 break;
454 case ByteOrder::BigEndian:
455 matched = BigEndian::readUint16(mCurrent) == aValue;
456 break;
457 default:
458 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
459 matched = false;
460 }
461
462 if (matched) {
463 Advance(2);
464 }
465
466 return matched;
467 }
468
ReadUInt16(uint16_t & aValue)469 bool EXIFParser::ReadUInt16(uint16_t& aValue) {
470 if (mRemainingLength < 2) {
471 return false;
472 }
473
474 bool matched = true;
475 switch (mByteOrder) {
476 case ByteOrder::LittleEndian:
477 aValue = LittleEndian::readUint16(mCurrent);
478 break;
479 case ByteOrder::BigEndian:
480 aValue = BigEndian::readUint16(mCurrent);
481 break;
482 default:
483 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
484 matched = false;
485 }
486
487 if (matched) {
488 Advance(2);
489 }
490
491 return matched;
492 }
493
ReadUInt32(uint32_t & aValue)494 bool EXIFParser::ReadUInt32(uint32_t& aValue) {
495 if (mRemainingLength < 4) {
496 return false;
497 }
498
499 bool matched = true;
500 switch (mByteOrder) {
501 case ByteOrder::LittleEndian:
502 aValue = LittleEndian::readUint32(mCurrent);
503 break;
504 case ByteOrder::BigEndian:
505 aValue = BigEndian::readUint32(mCurrent);
506 break;
507 default:
508 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
509 matched = false;
510 }
511
512 if (matched) {
513 Advance(4);
514 }
515
516 return matched;
517 }
518
519 } // namespace mozilla::image
520