2 * Copyright 2007-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
7 #include "exif_parser.h"
18 #include <ReadHelper.h>
20 #undef B_TRANSLATION_CONTEXT
21 #define B_TRANSLATION_CONTEXT "exit_parser"
27 TAG_EXIF_OFFSET
= 0x8769,
28 TAG_SUB_DIR_OFFSET
= 0xa005,
32 TAG_ORIENTATION
= 0x112,
33 TAG_EXPOSURE_TIME
= 0x829a,
37 static const convert_tag kDefaultTags
[] = {
38 {TAG_MAKER
, B_ANY_TYPE
, "Maker"},
39 {TAG_MODEL
, B_ANY_TYPE
, "Model"},
40 {TAG_ORIENTATION
, B_INT32_TYPE
, "Orientation"},
41 {TAG_EXPOSURE_TIME
, B_DOUBLE_TYPE
, "ExposureTime"},
42 {TAG_ISO
, B_INT32_TYPE
, "ISO"},
44 static const size_t kNumDefaultTags
= sizeof(kDefaultTags
)
45 / sizeof(kDefaultTags
[0]);
48 static status_t
parse_tiff_directory(TReadHelper
& read
, set
<off_t
>& visited
,
49 BMessage
& target
, const convert_tag
* tags
, size_t tagCount
);
53 add_to_message(TReadHelper
& source
, BMessage
& target
, tiff_tag
& tag
,
54 const char* name
, type_code type
)
56 type_code defaultType
= B_INT32_TYPE
;
57 double doubleValue
= 0.0;
61 case TIFF_STRING_TYPE
:
63 if (type
!= B_ANY_TYPE
&& type
!= B_STRING_TYPE
)
66 char* buffer
= (char*)malloc(tag
.length
);
70 source(buffer
, tag
.length
);
72 // remove trailing spaces
74 while ((--i
> 0 && isspace(buffer
[i
])) || !buffer
[i
]) {
78 status_t status
= target
.AddString(name
, buffer
);
84 case TIFF_UNDEFINED_TYPE
:
86 if (type
!= B_ANY_TYPE
&& type
!= B_STRING_TYPE
&& type
!= B_RAW_TYPE
)
89 char* buffer
= (char*)malloc(tag
.length
);
93 source(buffer
, tag
.length
);
96 if (type
== B_STRING_TYPE
)
97 status
= target
.AddString(name
, buffer
);
99 status
= target
.AddData(name
, B_RAW_TYPE
, buffer
, tag
.length
);
107 case TIFF_UINT8_TYPE
:
108 intValue
= source
.Next
<uint8
>();
110 case TIFF_UINT16_TYPE
:
111 defaultType
= B_INT32_TYPE
;
112 intValue
= source
.Next
<uint16
>();
114 case TIFF_UINT32_TYPE
:
115 defaultType
= B_INT32_TYPE
;
116 intValue
= source
.Next
<uint32
>();
118 case TIFF_UFRACTION_TYPE
:
120 defaultType
= B_DOUBLE_TYPE
;
121 double value
= source
.Next
<uint32
>();
122 doubleValue
= value
/ source
.Next
<uint32
>();
128 intValue
= source
.Next
<int8
>();
130 case TIFF_INT16_TYPE
:
131 intValue
= source
.Next
<int16
>();
133 case TIFF_INT32_TYPE
:
134 intValue
= source
.Next
<int32
>();
136 case TIFF_FRACTION_TYPE
:
138 defaultType
= B_DOUBLE_TYPE
;
139 double value
= source
.Next
<int32
>();
140 doubleValue
= value
/ source
.Next
<int32
>();
145 case TIFF_FLOAT_TYPE
:
146 defaultType
= B_FLOAT_TYPE
;
147 doubleValue
= source
.Next
<float>();
149 case TIFF_DOUBLE_TYPE
:
150 defaultType
= B_DOUBLE_TYPE
;
151 doubleValue
= source
.Next
<double>();
158 if (defaultType
== B_INT32_TYPE
)
159 doubleValue
= intValue
;
161 intValue
= int32(doubleValue
+ 0.5);
163 if (type
== B_ANY_TYPE
)
168 return target
.AddInt32(name
, intValue
);
170 return target
.AddFloat(name
, doubleValue
);
172 return target
.AddDouble(name
, doubleValue
);
180 static const convert_tag
*
181 find_convert_tag(uint16 id
, const convert_tag
* tags
, size_t count
)
183 for (size_t i
= 0; i
< count
; i
++) {
184 if (tags
[i
].tag
== id
)
193 Reads a TIFF tag and positions the file stream to its data section
196 parse_tiff_tag(TReadHelper
& read
, tiff_tag
& tag
, off_t
& offset
)
202 offset
= read
.Position() + 4;
204 uint32 length
= tag
.length
;
207 case TIFF_UINT16_TYPE
:
208 case TIFF_INT16_TYPE
:
212 case TIFF_UINT32_TYPE
:
213 case TIFF_INT32_TYPE
:
214 case TIFF_FLOAT_TYPE
:
218 case TIFF_UFRACTION_TYPE
:
219 case TIFF_FRACTION_TYPE
:
220 case TIFF_DOUBLE_TYPE
:
232 read
.Seek(position
, SEEK_SET
);
238 parse_tiff_directory(TReadHelper
& read
, set
<off_t
>& visited
, off_t offset
,
239 BMessage
& target
, const convert_tag
* convertTags
, size_t convertTagCount
)
241 if (visited
.find(offset
) != visited
.end()) {
242 // The EXIF data is obviously corrupt
246 read
.Seek(offset
, SEEK_SET
);
247 visited
.insert(offset
);
257 parse_tiff_tag(read
, tag
, nextOffset
);
259 //printf("TAG %u\n", tag.tag);
262 case TAG_EXIF_OFFSET
:
263 case TAG_SUB_DIR_OFFSET
:
265 status_t status
= parse_tiff_directory(read
, visited
, target
,
266 convertTags
, convertTagCount
);
273 const convert_tag
* convertTag
= find_convert_tag(tag
.tag
,
274 convertTags
, convertTagCount
);
275 if (convertTag
!= NULL
) {
276 add_to_message(read
, target
, tag
, convertTag
->name
,
282 if (visited
.find(nextOffset
) != visited
.end())
285 read
.Seek(nextOffset
, SEEK_SET
);
286 visited
.insert(nextOffset
);
294 parse_tiff_directory(TReadHelper
& read
, set
<off_t
>& visited
, BMessage
& target
,
295 const convert_tag
* tags
, size_t tagCount
)
303 status_t status
= parse_tiff_directory(read
, visited
, offset
, target
,
316 /*! Converts the EXIF data that starts in \a source to a BMessage in \a target.
317 If the EXIF data is corrupt, this function will return an appropriate error
318 code. Nevertheless, there might be some data ending up in \a target that
319 was parsed until this point.
322 convert_exif_to_message(BPositionIO
& source
, BMessage
& target
,
323 const convert_tag
* tags
, size_t tagCount
)
325 TReadHelper
read(source
);
329 if (endian
!= 'MM' && endian
!= 'II')
332 #if B_HOST_IS_LENDIAN
333 read
.SetSwap(endian
== 'MM');
335 read
.SetSwap(endian
== 'II');
343 set
<off_t
> visitedOffsets
;
344 return parse_tiff_directory(read
, visitedOffsets
, target
, tags
, tagCount
);
349 convert_exif_to_message(BPositionIO
& source
, BMessage
& target
)
351 return convert_exif_to_message(source
, target
, kDefaultTags
,