LibreOffice Module vcl (master)  1
Exif.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "Exif.hxx"
21 #include <memory>
22 #include <osl/endian.h>
23 #include <tools/stream.hxx>
24 
26  maOrientation(TOP_LEFT),
27  mbExifPresent(false)
28 {}
29 
31 {}
32 
33 
34 void Exif::setOrientation(Orientation aOrientation) {
35  maOrientation = aOrientation;
36 }
37 
39 {
40  switch(value) {
41  case 1: return TOP_LEFT;
42  case 2: return TOP_RIGHT;
43  case 3: return BOTTOM_RIGHT;
44  case 4: return BOTTOM_LEFT;
45  case 5: return LEFT_TOP;
46  case 6: return RIGHT_TOP;
47  case 7: return RIGHT_BOTTOM;
48  case 8: return LEFT_BOTTOM;
49  }
50  return TOP_LEFT;
51 }
52 
53 sal_Int32 Exif::getRotation() const
54 {
55  switch(maOrientation) {
56  case TOP_LEFT:
57  return 0;
58  case BOTTOM_RIGHT:
59  return 1800;
60  case RIGHT_TOP:
61  return 2700;
62  case LEFT_BOTTOM:
63  return 900;
64  default:
65  break;
66  }
67  return 0;
68 }
69 
70 
71 bool Exif::read(SvStream& rStream)
72 {
73  sal_Int32 nStreamPosition = rStream.Tell();
74  bool result = processJpeg(rStream, false);
75  rStream.Seek( nStreamPosition );
76 
77  return result;
78 }
79 
80 void Exif::write(SvStream& rStream)
81 {
82  sal_Int32 nStreamPosition = rStream.Tell();
83  processJpeg(rStream, true);
84  rStream.Seek( nStreamPosition );
85 }
86 
87 bool Exif::processJpeg(SvStream& rStream, bool bSetValue)
88 {
89  sal_uInt16 aMagic16;
90  sal_uInt16 aLength;
91 
92  sal_uInt32 aSize = rStream.TellEnd();
93  rStream.Seek(STREAM_SEEK_TO_BEGIN);
94 
95  rStream.SetEndian( SvStreamEndian::BIG );
96  rStream.ReadUInt16( aMagic16 );
97 
98  // Compare JPEG magic bytes
99  if( 0xFFD8 != aMagic16 )
100  {
101  return false;
102  }
103 
104  sal_uInt32 aPreviousPosition = STREAM_SEEK_TO_BEGIN;
105 
106  while(true)
107  {
108  sal_uInt8 aMarker = 0xD9;
109  sal_Int32 aCount;
110 
111  for (aCount = 0; aCount < 7; aCount++)
112  {
113  rStream.ReadUChar( aMarker );
114  if (aMarker != 0xFF)
115  {
116  break;
117  }
118  if (aCount >= 6)
119  {
120  return false;
121  }
122  }
123 
124  rStream.ReadUInt16( aLength );
125 
126  if (aLength < 8 || aLength > rStream.remainingSize())
127  {
128  return false;
129  }
130 
131  if (aMarker == 0xE1)
132  {
133  return processExif(rStream, aLength, bSetValue);
134  }
135  else if (aMarker == 0xD9)
136  {
137  return false;
138  }
139  else
140  {
141  sal_uInt32 aCurrentPosition = rStream.SeekRel(aLength-1);
142  if (aCurrentPosition == aPreviousPosition || aCurrentPosition > aSize)
143  {
144  return false;
145  }
146  aPreviousPosition = aCurrentPosition;
147  }
148  }
149  return false;
150 }
151 
152 namespace {
153 
154 sal_uInt16 read16(sal_uInt8 const (& data)[2], bool littleEndian) {
155  if (littleEndian) {
156  return data[0] | (sal_uInt16(data[1]) << 8);
157  } else {
158  return data[1] | (sal_uInt16(data[0]) << 8);
159  }
160 }
161 
162 void write16(sal_uInt16 value, sal_uInt8 (& data)[2], bool littleEndian) {
163  if (littleEndian) {
164  data[0] = value & 0xFF;
165  data[1] = value >> 8;
166  } else {
167  data[1] = value & 0xFF;
168  data[0] = value >> 8;
169  }
170 }
171 
172 sal_uInt32 read32(sal_uInt8 const (& data)[4], bool littleEndian) {
173  if (littleEndian) {
174  return data[0] | (sal_uInt32(data[1]) << 8)
175  | (sal_uInt32(data[2]) << 16) | (sal_uInt32(data[3]) << 24);
176  } else {
177  return data[3] | (sal_uInt32(data[2]) << 8)
178  | (sal_uInt32(data[1]) << 16) | (sal_uInt32(data[0]) << 24);
179  }
180 }
181 
182 void write32(sal_uInt32 value, sal_uInt8 (& data)[4], bool littleEndian) {
183  if (littleEndian) {
184  data[0] = value & 0xFF;
185  data[1] = (value >> 8) & 0xFF;
186  data[2] = (value >> 16) & 0xFF;
187  data[3] = value >> 24;
188  } else {
189  data[3] = value & 0xFF;
190  data[2] = (value >> 8) & 0xFF;
191  data[1] = (value >> 16) & 0xFF;
192  data[0] = value >> 24;
193  }
194 }
195 
196 }
197 
198 void Exif::processIFD(sal_uInt8* pExifData, sal_uInt16 aLength, sal_uInt16 aOffset, sal_uInt16 aNumberOfTags, bool bSetValue, bool littleEndian)
199 {
200  ExifIFD* ifd = nullptr;
201 
202  while (aOffset <= aLength - 12 && aNumberOfTags > 0)
203  {
204  ifd = reinterpret_cast<ExifIFD*>(&pExifData[aOffset]);
205  sal_uInt16 tag = read16(ifd->tag, littleEndian);
206 
207  if (tag == ORIENTATION)
208  {
209  if(bSetValue)
210  {
211  write16(3, ifd->type, littleEndian);
212  write32(1, ifd->count, littleEndian);
213  write32(maOrientation, ifd->offset, littleEndian);
214  }
215  else
216  {
217  sal_uInt32 nIfdOffset = read32(ifd->offset, littleEndian);
218  maOrientation = convertToOrientation(nIfdOffset);
219  }
220  }
221 
222  aNumberOfTags--;
223  aOffset += 12;
224  }
225 }
226 
227 bool Exif::processExif(SvStream& rStream, sal_uInt16 aSectionLength, bool bSetValue)
228 {
229  sal_uInt32 aMagic32;
230  sal_uInt16 aMagic16;
231 
232  rStream.ReadUInt32( aMagic32 );
233  rStream.ReadUInt16( aMagic16 );
234 
235  // Compare EXIF magic bytes
236  if( 0x45786966 != aMagic32 || 0x0000 != aMagic16)
237  {
238  return false;
239  }
240 
241  sal_uInt16 aLength = aSectionLength - 6; // Length = Section - Header
242 
243  std::unique_ptr<sal_uInt8[]> aExifData(new sal_uInt8[aLength]);
244  sal_uInt32 aExifDataBeginPosition = rStream.Tell();
245 
246  rStream.ReadBytes(aExifData.get(), aLength);
247 
248  // Exif detected
249  mbExifPresent = true;
250 
251  TiffHeader* aTiffHeader = reinterpret_cast<TiffHeader*>(&aExifData[0]);
252 
253  bool bIntel = aTiffHeader->byteOrder == 0x4949; //little-endian
254  bool bMotorola = aTiffHeader->byteOrder == 0x4D4D; //big-endian
255 
256  if (!bIntel && !bMotorola)
257  {
258  return false;
259  }
260 
261  bool bSwap = false;
262 
263 #ifdef OSL_BIGENDIAN
264  if (bIntel)
265  bSwap = true;
266 #else
267  if (bMotorola)
268  bSwap = true;
269 #endif
270 
271  if (bSwap)
272  {
273  aTiffHeader->tagAlign = OSL_SWAPWORD(aTiffHeader->tagAlign);
274  aTiffHeader->offset = OSL_SWAPDWORD(aTiffHeader->offset);
275  }
276 
277  if (aTiffHeader->tagAlign != 0x002A) // TIFF tag
278  {
279  return false;
280  }
281 
282  sal_uInt16 aOffset = aTiffHeader->offset;
283 
284  sal_uInt16 aNumberOfTags = aExifData[aOffset];
285  if (bSwap)
286  {
287  aNumberOfTags = ((aExifData[aOffset] << 8) | aExifData[aOffset+1]);
288  }
289 
290  processIFD(aExifData.get(), aLength, aOffset+2, aNumberOfTags, bSetValue, bIntel);
291 
292  if (bSetValue)
293  {
294  rStream.Seek(aExifDataBeginPosition);
295  rStream.WriteBytes(aExifData.get(), aLength);
296  }
297 
298  return true;
299 }
300 
301 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool processJpeg(SvStream &rStream, bool bSetValue)
Definition: Exif.cxx:87
Orientation
Definition: Exif.hxx:25
sal_uInt8 type[2]
Definition: Exif.hxx:52
void setOrientation(Orientation orientation)
Definition: Exif.cxx:34
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
sal_uInt8 offset[4]
Definition: Exif.hxx:54
void write(SvStream &rStream)
Definition: Exif.cxx:80
virtual sal_uInt64 TellEnd()
sal_uInt16 tagAlign
Definition: Exif.hxx:59
bool read(SvStream &rStream)
Definition: Exif.cxx:71
sal_uInt64 Seek(sal_uInt64 nPos)
Orientation maOrientation
Definition: Exif.hxx:43
~Exif()
Definition: Exif.cxx:30
sal_uInt64 SeekRel(sal_Int64 nPos)
sal_uInt64 remainingSize()
sal_uInt8 tag[2]
Definition: Exif.hxx:51
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
sal_uInt32 tag
Definition: ttcr.cxx:465
bool mbExifPresent
Definition: Exif.hxx:44
#define STREAM_SEEK_TO_BEGIN
sal_uInt32 offset
Definition: Exif.hxx:60
MetadataImporterPluginType * result
std::size_t WriteBytes(const void *pData, std::size_t nSize)
SvStream & ReadUChar(unsigned char &rChar)
static Orientation convertToOrientation(sal_Int32 value)
Definition: Exif.cxx:38
std::size_t ReadBytes(void *pData, std::size_t nSize)
unsigned char sal_uInt8
void SetEndian(SvStreamEndian SvStreamEndian)
void processIFD(sal_uInt8 *pExifData, sal_uInt16 aLength, sal_uInt16 aOffset, sal_uInt16 aNumberOfTags, bool bSetValue, bool bLittleEndian)
Definition: Exif.cxx:198
sal_uInt64 Tell() const
Exif()
Definition: Exif.cxx:25
bool processExif(SvStream &rStream, sal_uInt16 aLength, bool bSetValue)
Definition: Exif.cxx:227
sal_uInt16 const byteOrder
Definition: Exif.hxx:58
sal_Int32 getRotation() const
Definition: Exif.cxx:53
sal_uInt8 count[4]
Definition: Exif.hxx:53