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 
25 using namespace ::exif;
26 
28  maOrientation(TOP_LEFT),
29  mbExifPresent(false)
30 {}
31 
33 {}
34 
35 
36 void Exif::setOrientation(Orientation aOrientation) {
37  maOrientation = aOrientation;
38 }
39 
41 {
42  switch(value) {
43  case 1: return TOP_LEFT;
44  case 2: return TOP_RIGHT;
45  case 3: return BOTTOM_RIGHT;
46  case 4: return BOTTOM_LEFT;
47  case 5: return LEFT_TOP;
48  case 6: return RIGHT_TOP;
49  case 7: return RIGHT_BOTTOM;
50  case 8: return LEFT_BOTTOM;
51  }
52  return TOP_LEFT;
53 }
54 
55 sal_Int32 Exif::getRotation() const
56 {
57  switch(maOrientation) {
58  case TOP_LEFT:
59  return 0;
60  case BOTTOM_RIGHT:
61  return 1800;
62  case RIGHT_TOP:
63  return 2700;
64  case LEFT_BOTTOM:
65  return 900;
66  default:
67  break;
68  }
69  return 0;
70 }
71 
72 
73 bool Exif::read(SvStream& rStream)
74 {
75  sal_Int32 nStreamPosition = rStream.Tell();
76  bool result = processJpeg(rStream, false);
77  rStream.Seek( nStreamPosition );
78 
79  return result;
80 }
81 
82 void Exif::write(SvStream& rStream)
83 {
84  sal_Int32 nStreamPosition = rStream.Tell();
85  processJpeg(rStream, true);
86  rStream.Seek( nStreamPosition );
87 }
88 
89 bool Exif::processJpeg(SvStream& rStream, bool bSetValue)
90 {
91  sal_uInt16 aMagic16;
92  sal_uInt16 aLength;
93 
94  sal_uInt32 aSize = rStream.TellEnd();
95  rStream.Seek(STREAM_SEEK_TO_BEGIN);
96 
97  rStream.SetEndian( SvStreamEndian::BIG );
98  rStream.ReadUInt16( aMagic16 );
99 
100  // Compare JPEG magic bytes
101  if( 0xFFD8 != aMagic16 )
102  {
103  return false;
104  }
105 
106  sal_uInt32 aPreviousPosition = STREAM_SEEK_TO_BEGIN;
107 
108  while(true)
109  {
110  sal_uInt8 aMarker = 0xD9;
111  sal_Int32 aCount;
112 
113  for (aCount = 0; aCount < 7; aCount++)
114  {
115  rStream.ReadUChar( aMarker );
116  if (aMarker != 0xFF)
117  {
118  break;
119  }
120  if (aCount >= 6)
121  {
122  return false;
123  }
124  }
125 
126  rStream.ReadUInt16( aLength );
127 
128  if (aLength < 8 || aLength > rStream.remainingSize())
129  {
130  return false;
131  }
132 
133  if (aMarker == 0xE1)
134  {
135  return processExif(rStream, aLength, bSetValue);
136  }
137  else if (aMarker == 0xD9)
138  {
139  return false;
140  }
141  else
142  {
143  sal_uInt32 aCurrentPosition = rStream.SeekRel(aLength-1);
144  if (aCurrentPosition == aPreviousPosition || aCurrentPosition > aSize)
145  {
146  return false;
147  }
148  aPreviousPosition = aCurrentPosition;
149  }
150  }
151  return false;
152 }
153 
154 namespace {
155 
156 sal_uInt16 read16(sal_uInt8 const (& data)[2], bool littleEndian) {
157  if (littleEndian) {
158  return data[0] | (sal_uInt16(data[1]) << 8);
159  } else {
160  return data[1] | (sal_uInt16(data[0]) << 8);
161  }
162 }
163 
164 void write16(sal_uInt16 value, sal_uInt8 (& data)[2], bool littleEndian) {
165  if (littleEndian) {
166  data[0] = value & 0xFF;
167  data[1] = value >> 8;
168  } else {
169  data[1] = value & 0xFF;
170  data[0] = value >> 8;
171  }
172 }
173 
174 void write32(sal_uInt32 value, sal_uInt8 (& data)[4], bool littleEndian) {
175  if (littleEndian) {
176  data[0] = value & 0xFF;
177  data[1] = (value >> 8) & 0xFF;
178  data[2] = (value >> 16) & 0xFF;
179  data[3] = value >> 24;
180  } else {
181  data[3] = value & 0xFF;
182  data[2] = (value >> 8) & 0xFF;
183  data[1] = (value >> 16) & 0xFF;
184  data[0] = value >> 24;
185  }
186 }
187 
188 }
189 
190 void Exif::processIFD(sal_uInt8* pExifData, sal_uInt16 aLength, sal_uInt16 aOffset, sal_uInt16 aNumberOfTags, bool bSetValue, bool littleEndian)
191 {
192  ExifIFD* ifd = nullptr;
193 
194  while (aOffset <= aLength - 12 && aNumberOfTags > 0)
195  {
196  ifd = reinterpret_cast<ExifIFD*>(&pExifData[aOffset]);
197  sal_uInt16 tag = read16(ifd->tag, littleEndian);
198 
199  if (tag == ORIENTATION)
200  {
201  if(bSetValue)
202  {
203  write16(3, ifd->type, littleEndian);
204  write32(1, ifd->count, littleEndian);
205  write16(
206  maOrientation, reinterpret_cast<sal_uInt8 (&)[2]>(ifd->offset), littleEndian);
207  }
208  else
209  {
210  sal_uInt16 nIfdOffset = read16(
211  reinterpret_cast<sal_uInt8 (&)[2]>(ifd->offset), littleEndian);
212  maOrientation = convertToOrientation(nIfdOffset);
213  }
214  }
215 
216  aNumberOfTags--;
217  aOffset += 12;
218  }
219 }
220 
221 bool Exif::processExif(SvStream& rStream, sal_uInt16 aSectionLength, bool bSetValue)
222 {
223  sal_uInt32 aMagic32;
224  sal_uInt16 aMagic16;
225 
226  rStream.ReadUInt32( aMagic32 );
227  rStream.ReadUInt16( aMagic16 );
228 
229  // Compare EXIF magic bytes
230  if( 0x45786966 != aMagic32 || 0x0000 != aMagic16)
231  {
232  return false;
233  }
234 
235  sal_uInt16 aLength = aSectionLength - 6; // Length = Section - Header
236 
237  std::unique_ptr<sal_uInt8[]> aExifData(new sal_uInt8[aLength]);
238  sal_uInt32 aExifDataBeginPosition = rStream.Tell();
239 
240  rStream.ReadBytes(aExifData.get(), aLength);
241 
242  // Exif detected
243  mbExifPresent = true;
244 
245  TiffHeader* aTiffHeader = reinterpret_cast<TiffHeader*>(&aExifData[0]);
246 
247  bool bIntel = aTiffHeader->byteOrder == 0x4949; //little-endian
248  bool bMotorola = aTiffHeader->byteOrder == 0x4D4D; //big-endian
249 
250  if (!bIntel && !bMotorola)
251  {
252  return false;
253  }
254 
255  bool bSwap = false;
256 
257 #ifdef OSL_BIGENDIAN
258  if (bIntel)
259  bSwap = true;
260 #else
261  if (bMotorola)
262  bSwap = true;
263 #endif
264 
265  if (bSwap)
266  {
267  aTiffHeader->tagAlign = OSL_SWAPWORD(aTiffHeader->tagAlign);
268  aTiffHeader->offset = OSL_SWAPDWORD(aTiffHeader->offset);
269  }
270 
271  if (aTiffHeader->tagAlign != 0x002A) // TIFF tag
272  {
273  return false;
274  }
275 
276  sal_uInt16 aOffset = aTiffHeader->offset;
277 
278  sal_uInt16 aNumberOfTags = aExifData[aOffset];
279  if (bSwap)
280  {
281  aNumberOfTags = ((aExifData[aOffset] << 8) | aExifData[aOffset+1]);
282  }
283 
284  processIFD(aExifData.get(), aLength, aOffset+2, aNumberOfTags, bSetValue, bIntel);
285 
286  if (bSetValue)
287  {
288  rStream.Seek(aExifDataBeginPosition);
289  rStream.WriteBytes(aExifData.get(), aLength);
290  }
291 
292  return true;
293 }
294 
295 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool processJpeg(SvStream &rStream, bool bSetValue)
Definition: Exif.cxx:89
Definition: Exif.hxx:24
sal_uInt8 type[2]
Definition: Exif.hxx:54
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
sal_uInt8 offset[4]
Definition: Exif.hxx:56
void write(SvStream &rStream)
Definition: Exif.cxx:82
virtual sal_uInt64 TellEnd()
sal_uInt16 tagAlign
Definition: Exif.hxx:61
bool read(SvStream &rStream)
Definition: Exif.cxx:73
sal_uInt64 Seek(sal_uInt64 nPos)
~Exif()
Definition: Exif.cxx:32
sal_uInt64 SeekRel(sal_Int64 nPos)
sal_uInt16 byteOrder
Definition: Exif.hxx:60
sal_uInt64 remainingSize()
void setOrientation(exif::Orientation orientation)
Definition: Exif.cxx:36
sal_uInt8 tag[2]
Definition: Exif.hxx:53
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
sal_uInt32 tag
Definition: ttcr.cxx:482
bool mbExifPresent
Definition: Exif.hxx:46
#define STREAM_SEEK_TO_BEGIN
sal_uInt32 offset
Definition: Exif.hxx:62
exif::Orientation maOrientation
Definition: Exif.hxx:45
std::size_t WriteBytes(const void *pData, std::size_t nSize)
SvStream & ReadUChar(unsigned char &rChar)
static exif::Orientation convertToOrientation(sal_Int32 value)
Definition: Exif.cxx:40
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:190
Orientation
Definition: prntypes.hxx:31
sal_uInt64 Tell() const
Exif()
Definition: Exif.cxx:27
Any result
bool processExif(SvStream &rStream, sal_uInt16 aLength, bool bSetValue)
Definition: Exif.cxx:221
sal_Int32 getRotation() const
Definition: Exif.cxx:55
sal_uInt8 count[4]
Definition: Exif.hxx:55