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