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