1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
22 #include <osl/endian.h>
23 #include <tools/stream.hxx>
26 maOrientation(TOP_LEFT
),
34 void Exif::setOrientation(Orientation aOrientation
) {
35 maOrientation
= aOrientation
;
38 Orientation
Exif::convertToOrientation(sal_Int32 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
;
53 sal_Int32
Exif::getRotation() const
55 switch(maOrientation
) {
71 bool Exif::read(SvStream
& rStream
)
73 sal_Int32 nStreamPosition
= rStream
.Tell();
74 bool result
= processJpeg(rStream
, false);
75 rStream
.Seek( nStreamPosition
);
80 void Exif::write(SvStream
& rStream
)
82 sal_Int32 nStreamPosition
= rStream
.Tell();
83 processJpeg(rStream
, true);
84 rStream
.Seek( nStreamPosition
);
87 bool Exif::processJpeg(SvStream
& rStream
, bool bSetValue
)
92 sal_uInt32 aSize
= rStream
.TellEnd();
93 rStream
.Seek(STREAM_SEEK_TO_BEGIN
);
95 rStream
.SetEndian( SvStreamEndian::BIG
);
96 rStream
.ReadUInt16( aMagic16
);
98 // Compare JPEG magic bytes
99 if( 0xFFD8 != aMagic16
)
104 sal_uInt32 aPreviousPosition
= STREAM_SEEK_TO_BEGIN
;
108 sal_uInt8 aMarker
= 0xD9;
111 for (aCount
= 0; aCount
< 7; aCount
++)
113 rStream
.ReadUChar( aMarker
);
124 rStream
.ReadUInt16( aLength
);
126 if (aLength
< 8 || aLength
> rStream
.remainingSize())
133 return processExif(rStream
, aLength
, bSetValue
);
135 else if (aMarker
== 0xD9)
141 sal_uInt32 aCurrentPosition
= rStream
.SeekRel(aLength
-1);
142 if (aCurrentPosition
== aPreviousPosition
|| aCurrentPosition
> aSize
)
146 aPreviousPosition
= aCurrentPosition
;
154 sal_uInt16
read16(sal_uInt8
const (& data
)[2], bool littleEndian
) {
156 return data
[0] | (sal_uInt16(data
[1]) << 8);
158 return data
[1] | (sal_uInt16(data
[0]) << 8);
162 void write16(sal_uInt16 value
, sal_uInt8 (& data
)[2], bool littleEndian
) {
164 data
[0] = value
& 0xFF;
165 data
[1] = value
>> 8;
167 data
[1] = value
& 0xFF;
168 data
[0] = value
>> 8;
172 sal_uInt32
read32(sal_uInt8
const (& data
)[4], bool littleEndian
) {
174 return data
[0] | (sal_uInt32(data
[1]) << 8)
175 | (sal_uInt32(data
[2]) << 16) | (sal_uInt32(data
[3]) << 24);
177 return data
[3] | (sal_uInt32(data
[2]) << 8)
178 | (sal_uInt32(data
[1]) << 16) | (sal_uInt32(data
[0]) << 24);
182 void write32(sal_uInt32 value
, sal_uInt8 (& data
)[4], bool littleEndian
) {
184 data
[0] = value
& 0xFF;
185 data
[1] = (value
>> 8) & 0xFF;
186 data
[2] = (value
>> 16) & 0xFF;
187 data
[3] = value
>> 24;
189 data
[3] = value
& 0xFF;
190 data
[2] = (value
>> 8) & 0xFF;
191 data
[1] = (value
>> 16) & 0xFF;
192 data
[0] = value
>> 24;
198 void Exif::processIFD(sal_uInt8
* pExifData
, sal_uInt16 aLength
, sal_uInt16 aOffset
, sal_uInt16 aNumberOfTags
, bool bSetValue
, bool littleEndian
)
200 ExifIFD
* ifd
= nullptr;
202 while (aOffset
<= aLength
- 12 && aNumberOfTags
> 0)
204 ifd
= reinterpret_cast<ExifIFD
*>(&pExifData
[aOffset
]);
205 sal_uInt16 tag
= read16(ifd
->tag
, littleEndian
);
207 if (tag
== ORIENTATION
)
211 write16(3, ifd
->type
, littleEndian
);
212 write32(1, ifd
->count
, littleEndian
);
213 write32(maOrientation
, ifd
->offset
, littleEndian
);
217 sal_uInt32 nIfdOffset
= read32(ifd
->offset
, littleEndian
);
218 maOrientation
= convertToOrientation(nIfdOffset
);
227 bool Exif::processExif(SvStream
& rStream
, sal_uInt16 aSectionLength
, bool bSetValue
)
232 rStream
.ReadUInt32( aMagic32
);
233 rStream
.ReadUInt16( aMagic16
);
235 // Compare EXIF magic bytes
236 if( 0x45786966 != aMagic32
|| 0x0000 != aMagic16
)
241 sal_uInt16 aLength
= aSectionLength
- 6; // Length = Section - Header
243 std::unique_ptr
<sal_uInt8
[]> aExifData(new sal_uInt8
[aLength
]);
244 sal_uInt32 aExifDataBeginPosition
= rStream
.Tell();
246 rStream
.ReadBytes(aExifData
.get(), aLength
);
249 mbExifPresent
= true;
251 TiffHeader
* aTiffHeader
= reinterpret_cast<TiffHeader
*>(&aExifData
[0]);
253 bool bIntel
= aTiffHeader
->byteOrder
== 0x4949; //little-endian
254 bool bMotorola
= aTiffHeader
->byteOrder
== 0x4D4D; //big-endian
256 if (!bIntel
&& !bMotorola
)
273 aTiffHeader
->tagAlign
= OSL_SWAPWORD(aTiffHeader
->tagAlign
);
274 aTiffHeader
->offset
= OSL_SWAPDWORD(aTiffHeader
->offset
);
277 if (aTiffHeader
->tagAlign
!= 0x002A) // TIFF tag
282 sal_uInt16 aOffset
= aTiffHeader
->offset
;
284 sal_uInt16 aNumberOfTags
= aExifData
[aOffset
];
287 aNumberOfTags
= ((aExifData
[aOffset
] << 8) | aExifData
[aOffset
+1]);
290 processIFD(aExifData
.get(), aLength
, aOffset
+2, aNumberOfTags
, bSetValue
, bIntel
);
294 rStream
.Seek(aExifDataBeginPosition
);
295 rStream
.WriteBytes(aExifData
.get(), aLength
);
301 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */