Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / vcl / source / filter / jpeg / Exif.cxx
blob469281bdcc85a4da65bb5ecf4aea5ef80f759146
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "Exif.hxx"
21 #include <memory>
22 #include <osl/endian.h>
23 #include <tools/stream.hxx>
25 Exif::Exif() :
26 maOrientation(exif::TOP_LEFT),
27 mbExifPresent(false)
30 void Exif::setOrientation(exif::Orientation aOrientation) {
31 maOrientation = aOrientation;
34 exif::Orientation Exif::convertToOrientation(sal_Int32 value)
36 switch(value) {
37 case 1: return exif::TOP_LEFT;
38 case 2: return exif::TOP_RIGHT;
39 case 3: return exif::BOTTOM_RIGHT;
40 case 4: return exif::BOTTOM_LEFT;
41 case 5: return exif::LEFT_TOP;
42 case 6: return exif::RIGHT_TOP;
43 case 7: return exif::RIGHT_BOTTOM;
44 case 8: return exif::LEFT_BOTTOM;
46 return exif::TOP_LEFT;
49 Degree10 Exif::getRotation() const
51 switch(maOrientation) {
52 case exif::TOP_LEFT:
53 return 0_deg10;
54 case exif::BOTTOM_RIGHT:
55 return 1800_deg10;
56 case exif::RIGHT_TOP:
57 return 2700_deg10;
58 case exif::LEFT_BOTTOM:
59 return 900_deg10;
60 default:
61 break;
63 return 0_deg10;
66 bool Exif::read(SvStream& rStream)
68 sal_uInt64 nStreamPosition = rStream.Tell();
69 bool result = processJpeg(rStream, false);
70 rStream.Seek( nStreamPosition );
72 return result;
75 void Exif::write(SvStream& rStream)
77 sal_uInt64 nStreamPosition = rStream.Tell();
78 processJpeg(rStream, true);
79 rStream.Seek( nStreamPosition );
82 bool Exif::processJpeg(SvStream& rStream, bool bSetValue)
84 sal_uInt16 aMagic16;
85 sal_uInt16 aLength;
87 sal_uInt64 aSize = rStream.TellEnd();
88 rStream.Seek(STREAM_SEEK_TO_BEGIN);
90 rStream.SetEndian( SvStreamEndian::BIG );
91 rStream.ReadUInt16( aMagic16 );
93 // Compare JPEG magic bytes
94 if( 0xFFD8 != aMagic16 )
96 return false;
99 sal_uInt32 aPreviousPosition = STREAM_SEEK_TO_BEGIN;
101 while(true)
103 sal_uInt8 aMarker = 0xD9;
104 sal_Int32 aCount;
106 for (aCount = 0; aCount < 7; aCount++)
108 rStream.ReadUChar( aMarker );
109 if (aMarker != 0xFF)
111 break;
113 if (aCount >= 6)
115 return false;
119 rStream.ReadUInt16( aLength );
121 if (aLength < 8 || aLength > rStream.remainingSize())
123 return false;
126 if (aMarker == 0xE1)
128 return processExif(rStream, aLength, bSetValue);
130 else if (aMarker == 0xD9)
132 return false;
134 else
136 sal_uInt64 aCurrentPosition = rStream.SeekRel(aLength-1);
137 if (aCurrentPosition == aPreviousPosition || aCurrentPosition > aSize)
139 return false;
141 aPreviousPosition = aCurrentPosition;
144 return false;
147 namespace {
149 sal_uInt16 read16(sal_uInt8 const (& data)[2], bool littleEndian) {
150 if (littleEndian) {
151 return data[0] | (sal_uInt16(data[1]) << 8);
152 } else {
153 return data[1] | (sal_uInt16(data[0]) << 8);
157 void write16(sal_uInt16 value, sal_uInt8 (& data)[2], bool littleEndian) {
158 if (littleEndian) {
159 data[0] = value & 0xFF;
160 data[1] = value >> 8;
161 } else {
162 data[1] = value & 0xFF;
163 data[0] = value >> 8;
167 void write32(sal_uInt32 value, sal_uInt8 (& data)[4], bool littleEndian) {
168 if (littleEndian) {
169 data[0] = value & 0xFF;
170 data[1] = (value >> 8) & 0xFF;
171 data[2] = (value >> 16) & 0xFF;
172 data[3] = value >> 24;
173 } else {
174 data[3] = value & 0xFF;
175 data[2] = (value >> 8) & 0xFF;
176 data[1] = (value >> 16) & 0xFF;
177 data[0] = value >> 24;
183 void Exif::processIFD(sal_uInt8* pExifData, sal_uInt16 aLength, sal_uInt16 aOffset, sal_uInt16 aNumberOfTags, bool bSetValue, bool littleEndian)
185 ExifIFD* ifd = nullptr;
187 while (aOffset <= aLength - 12 && aNumberOfTags > 0)
189 ifd = reinterpret_cast<ExifIFD*>(&pExifData[aOffset]);
190 sal_uInt16 tag = read16(ifd->tag, littleEndian);
192 if (tag == ORIENTATION)
194 if(bSetValue)
196 write16(3, ifd->type, littleEndian);
197 write32(1, ifd->count, littleEndian);
198 write16(
199 maOrientation, reinterpret_cast<sal_uInt8 (&)[2]>(ifd->offset), littleEndian);
201 else
203 sal_uInt16 nIfdOffset = read16(
204 reinterpret_cast<sal_uInt8 (&)[2]>(ifd->offset), littleEndian);
205 maOrientation = convertToOrientation(nIfdOffset);
209 aNumberOfTags--;
210 aOffset += 12;
214 bool Exif::processExif(SvStream& rStream, sal_uInt16 aSectionLength, bool bSetValue)
216 sal_uInt32 aMagic32;
217 sal_uInt16 aMagic16;
219 rStream.ReadUInt32( aMagic32 );
220 rStream.ReadUInt16( aMagic16 );
222 // Compare EXIF magic bytes
223 if( 0x45786966 != aMagic32 || 0x0000 != aMagic16)
225 return false;
228 sal_uInt16 aLength = aSectionLength - 6; // Length = Section - Header
230 std::unique_ptr<sal_uInt8[]> aExifData(new sal_uInt8[aLength]);
231 sal_uInt64 aExifDataBeginPosition = rStream.Tell();
233 rStream.ReadBytes(aExifData.get(), aLength);
235 // Exif detected
236 mbExifPresent = true;
238 TiffHeader* aTiffHeader = reinterpret_cast<TiffHeader*>(&aExifData[0]);
240 bool bIntel = aTiffHeader->byteOrder == 0x4949; //little-endian
241 bool bMotorola = aTiffHeader->byteOrder == 0x4D4D; //big-endian
243 if (!bIntel && !bMotorola)
245 return false;
248 bool bSwap = false;
250 #ifdef OSL_BIGENDIAN
251 if (bIntel)
252 bSwap = true;
253 #else
254 if (bMotorola)
255 bSwap = true;
256 #endif
258 if (bSwap)
260 aTiffHeader->tagAlign = OSL_SWAPWORD(aTiffHeader->tagAlign);
261 aTiffHeader->offset = OSL_SWAPDWORD(aTiffHeader->offset);
264 if (aTiffHeader->tagAlign != 0x002A) // TIFF tag
266 return false;
269 sal_uInt16 aOffset = aTiffHeader->offset;
271 sal_uInt16 aNumberOfTags = aExifData[aOffset];
272 if (bSwap)
274 aNumberOfTags = ((aExifData[aOffset] << 8) | aExifData[aOffset+1]);
277 processIFD(aExifData.get(), aLength, aOffset+2, aNumberOfTags, bSetValue, bIntel);
279 if (bSetValue)
281 rStream.Seek(aExifDataBeginPosition);
282 rStream.WriteBytes(aExifData.get(), aLength);
285 return true;
288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */