xref: /haiku/src/add-ons/translators/jpeg/exif_parser.cpp (revision 93a78ecaa45114d68952d08c4778f073515102f2)
1 /*
2  * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "exif_parser.h"
8 
9 #include <ReadHelper.h>
10 
11 #include <Message.h>
12 
13 #include <ctype.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 
17 
18 enum {
19 	TAG_EXIF_OFFSET		= 0x8769,
20 	TAG_SUB_DIR_OFFSET	= 0xa005,
21 
22 	TAG_MAKER			= 0x10f,
23 	TAG_MODEL			= 0x110,
24 	TAG_ORIENTATION		= 0x112,
25 	TAG_EXPOSURE_TIME	= 0x829a,
26 	TAG_ISO				= 0x8827,
27 };
28 
29 static const convert_tag kDefaultTags[] = {
30 	{TAG_MAKER,			B_ANY_TYPE,		"Maker"},
31 	{TAG_MODEL,			B_ANY_TYPE,		"Model"},
32 	{TAG_ORIENTATION,	B_INT32_TYPE,	"Orientation"},
33 	{TAG_EXPOSURE_TIME,	B_DOUBLE_TYPE,	"ExposureTime"},
34 	{TAG_ISO,			B_INT32_TYPE,	"ISO"},
35 };
36 static const size_t kNumDefaultTags = sizeof(kDefaultTags)
37 	/ sizeof(kDefaultTags[0]);
38 
39 
40 static status_t parse_tiff_directory(TReadHelper& read, BMessage& target,
41 	const convert_tag* tags, size_t tagCount);
42 
43 
44 static status_t
45 add_to_message(TReadHelper& source, BMessage& target, tiff_tag& tag,
46 	const char* name, type_code type)
47 {
48 	type_code defaultType = B_INT32_TYPE;
49 	double doubleValue = 0.0;
50 	int32 intValue = 0;
51 
52 	switch (tag.type) {
53 		case TIFF_STRING_TYPE:
54 		{
55 			if (type != B_ANY_TYPE && type != B_STRING_TYPE)
56 				return B_BAD_VALUE;
57 
58 			char* buffer = (char*)malloc(tag.length);
59 			if (buffer == NULL)
60 				return B_NO_MEMORY;
61 
62 			source(buffer, tag.length);
63 
64 			// remove trailing spaces
65 			int32 i = tag.length;
66 			while (--i > 0 && isspace(buffer[i]) || !buffer[i]) {
67 				buffer[i] = '\0';
68 			}
69 
70 			status_t status = target.AddString(name, buffer);
71 			free(buffer);
72 
73 			return status;
74 		}
75 
76 		case TIFF_UNDEFINED_TYPE:
77 		{
78 			if (type != B_ANY_TYPE && type != B_STRING_TYPE && type != B_RAW_TYPE)
79 				return B_BAD_VALUE;
80 
81 			char* buffer = (char*)malloc(tag.length);
82 			if (buffer == NULL)
83 				return B_NO_MEMORY;
84 
85 			source(buffer, tag.length);
86 
87 			status_t status;
88 			if (type == B_STRING_TYPE)
89 				status = target.AddString(name, buffer);
90 			else
91 				status = target.AddData(name, B_RAW_TYPE, buffer, tag.length);
92 
93 			free(buffer);
94 
95 			return status;
96 		}
97 
98 		// unsigned
99 		case TIFF_UINT8_TYPE:
100 			intValue = source.Next<uint8>();
101 			break;
102 		case TIFF_UINT16_TYPE:
103 			defaultType = B_INT32_TYPE;
104 			intValue = source.Next<uint16>();
105 			break;
106 		case TIFF_UINT32_TYPE:
107 			defaultType = B_INT32_TYPE;
108 			intValue = source.Next<uint32>();
109 			break;
110 		case TIFF_UFRACTION_TYPE:
111 		{
112 			defaultType = B_DOUBLE_TYPE;
113 			double value = source.Next<uint32>();
114 			doubleValue = value / source.Next<uint32>();
115 			break;
116 		}
117 
118 		// signed
119 		case TIFF_INT8_TYPE:
120 			intValue = source.Next<int8>();
121 			break;
122 		case TIFF_INT16_TYPE:
123 			intValue = source.Next<int16>();
124 			break;
125 		case TIFF_INT32_TYPE:
126 			intValue = source.Next<int32>();
127 			break;
128 		case TIFF_FRACTION_TYPE:
129 		{
130 			defaultType = B_DOUBLE_TYPE;
131 			double value = source.Next<int32>();
132 			doubleValue = value / source.Next<int32>();
133 		}
134 
135 		// floating point
136 		case TIFF_FLOAT_TYPE:
137 			defaultType = B_FLOAT_TYPE;
138 			doubleValue = source.Next<float>();
139 			break;
140 		case TIFF_DOUBLE_TYPE:
141 			defaultType = B_DOUBLE_TYPE;
142 			doubleValue = source.Next<double>();
143 			break;
144 
145 		default:
146 			return B_BAD_VALUE;
147 	}
148 
149 	if (defaultType == B_INT32_TYPE)
150 		doubleValue = intValue;
151 	else
152 		intValue = int32(doubleValue + 0.5);
153 
154 	if (type == B_ANY_TYPE)
155 		type = defaultType;
156 
157 	switch (type) {
158 		case B_INT32_TYPE:
159 			return target.AddInt32(name, intValue);
160 		case B_FLOAT_TYPE:
161 			return target.AddFloat(name, doubleValue);
162 		case B_DOUBLE_TYPE:
163 			return target.AddDouble(name, doubleValue);
164 
165 		default:
166 			return B_BAD_VALUE;
167 	}
168 }
169 
170 
171 static const convert_tag*
172 find_convert_tag(uint16 id, const convert_tag* tags, size_t count)
173 {
174 	for (size_t i = 0; i < count; i++) {
175 		if (tags[i].tag == id)
176 			return &tags[i];
177 	}
178 
179 	return NULL;
180 }
181 
182 
183 /*!
184 	Reads a TIFF tag and positions the file stream to its data section
185 */
186 void
187 parse_tiff_tag(TReadHelper& read, tiff_tag& tag, off_t& offset)
188 {
189 	read(tag.tag);
190 	read(tag.type);
191 	read(tag.length);
192 
193 	offset = read.Position() + 4;
194 
195 	uint32 length = tag.length;
196 
197 	switch (tag.type) {
198 		case TIFF_UINT16_TYPE:
199 		case TIFF_INT16_TYPE:
200 			length *= 2;
201 			break;
202 
203 		case TIFF_UINT32_TYPE:
204 		case TIFF_INT32_TYPE:
205 		case TIFF_FLOAT_TYPE:
206 			length *= 4;
207 			break;
208 
209 		case TIFF_UFRACTION_TYPE:
210 		case TIFF_FRACTION_TYPE:
211 		case TIFF_DOUBLE_TYPE:
212 			length *= 8;
213 			break;
214 
215 		default:
216 			break;
217 	}
218 
219 	if (length > 4) {
220 		uint32 position;
221 		read(position);
222 
223 		read.Seek(position, SEEK_SET);
224 	}
225 }
226 
227 
228 static status_t
229 parse_tiff_directory(TReadHelper& read, off_t offset, BMessage& target,
230 	const convert_tag* convertTags, size_t convertTagCount)
231 {
232 	read.Seek(offset, SEEK_SET);
233 
234 	uint16 tags;
235 	read(tags);
236 	if (tags > 512)
237 		return B_BAD_DATA;
238 
239 	while (tags--) {
240 		off_t nextOffset;
241 		tiff_tag tag;
242 		parse_tiff_tag(read, tag, nextOffset);
243 
244 		//printf("TAG %u\n", tag.tag);
245 
246 		switch (tag.tag) {
247 			case TAG_EXIF_OFFSET:
248 			case TAG_SUB_DIR_OFFSET:
249 			{
250 				status_t status = parse_tiff_directory(read, target,
251 					convertTags, convertTagCount);
252 				if (status < B_OK)
253 					return status;
254 				break;
255 			}
256 
257 			default:
258 				const convert_tag* convertTag = find_convert_tag(tag.tag,
259 					convertTags, convertTagCount);
260 				if (convertTag != NULL) {
261 					add_to_message(read, target, tag, convertTag->name,
262 						convertTag->type);
263 				}
264 				break;
265 		}
266 		read.Seek(nextOffset, SEEK_SET);
267 	}
268 
269 	return B_OK;
270 }
271 
272 
273 static status_t
274 parse_tiff_directory(TReadHelper& read, BMessage& target,
275 	const convert_tag* tags, size_t tagCount)
276 {
277 	while (true) {
278 		int32 offset;
279 		read(offset);
280 		if (offset == 0)
281 			break;
282 
283 		status_t status = parse_tiff_directory(read, offset, target,
284 			tags, tagCount);
285 		if (status < B_OK)
286 			return status;
287 	}
288 
289 	return B_OK;
290 }
291 
292 
293 //	#pragma mark -
294 
295 
296 status_t
297 convert_exif_to_message(BPositionIO& source, BMessage& target,
298 	const convert_tag* tags, size_t tagCount)
299 {
300 	TReadHelper read(source);
301 
302 	uint16 endian;
303 	read(endian);
304 	if (endian != 'MM' && endian != 'II')
305 		return B_BAD_TYPE;
306 
307 #if B_HOST_IS_LENDIAN
308 	read.SetSwap(endian == 'MM');
309 #else
310 	read.SetSwap(endian == 'II');
311 #endif
312 
313 	int16 magic;
314 	read(magic);
315 	if (magic != 42)
316 		return B_BAD_TYPE;
317 
318 	return parse_tiff_directory(read, target, tags, tagCount);
319 }
320 
321 
322 status_t
323 convert_exif_to_message(BPositionIO& source, BMessage& target)
324 {
325 	return convert_exif_to_message(source, target, kDefaultTags,
326 		kNumDefaultTags);
327 }
328