]> git.imager.perl.org - imager.git/blob - JPEG/imexif.c
Update metadata to meta-spec 2, include web view of repository, correct EUMM version...
[imager.git] / JPEG / imexif.c
1 #include "imext.h"
2 #include "imexif.h"
3 #include <stdlib.h>
4 #include <float.h>
5 #include <string.h>
6 #include <stdio.h>
7
8 /*
9 =head1 NAME
10
11 imexif.c - EXIF support for Imager
12
13 =head1 SYNOPSIS
14
15   if (i_int_decode_exif(im, app1data, app1datasize)) {
16     // exif block seen
17   }
18
19 =head1 DESCRIPTION
20
21 This code provides a basic EXIF data decoder.  It is intended to be
22 called from the JPEG reader code when an APP1 data block is found, and
23 will set tags in the supplied image.
24
25 =cut
26 */
27
28 typedef enum tiff_type_tag {
29   tt_intel = 'I',
30   tt_motorola = 'M'
31 } tiff_type;
32
33 typedef enum {
34   ift_byte = 1,
35   ift_ascii = 2,
36   ift_short = 3,
37   ift_long = 4,
38   ift_rational = 5,
39   ift_sbyte = 6,
40   ift_undefined = 7,
41   ift_sshort = 8,
42   ift_slong = 9,
43   ift_srational = 10,
44   ift_float = 11,
45   ift_double = 12,
46   ift_last = 12 /* keep the same as the highest type code */
47 } ifd_entry_type;
48
49 static int type_sizes[] =
50   {
51     0, /* not used */
52     1, /* byte */
53     1, /* ascii */
54     2, /* short */
55     4, /* long */
56     8, /* rational */
57     1, /* sbyte */
58     1, /* undefined */
59     2, /* sshort */
60     4, /* slong */
61     8, /* srational */
62     4, /* float */
63     8, /* double */
64   };
65
66 typedef struct {
67   int tag;
68   int type;
69   int count;
70   int item_size;
71   int size;
72   int offset;
73 } ifd_entry;
74
75 typedef struct {
76   int tag;
77   char const *name;
78 } tag_map;
79
80 typedef struct {
81   int tag;
82   char const *name;
83   tag_map const *map;
84   int map_count;
85 } tag_value_map;
86
87 #define PASTE(left, right) PASTE_(left, right)
88 #define PASTE_(left, right) left##right
89 #define QUOTE(value) #value
90
91 #define VALUE_MAP_ENTRY(name) \
92   { \
93     PASTE(tag_, name), \
94     "exif_" QUOTE(name) "_name", \
95     PASTE(name, _values), \
96     ARRAY_COUNT(PASTE(name, _values)) \
97   }
98
99 /* we don't process every tag */
100 #define tag_make 271
101 #define tag_model 272
102 #define tag_orientation 274
103 #define tag_x_resolution 282
104 #define tag_y_resolution 283
105 #define tag_resolution_unit 296
106 #define tag_copyright 33432
107 #define tag_software 305
108 #define tag_artist 315
109 #define tag_date_time 306
110 #define tag_image_description 270
111
112 #define tag_exif_ifd 34665
113 #define tag_gps_ifd 34853
114
115 #define resunit_none 1
116 #define resunit_inch 2
117 #define resunit_centimeter 3
118
119 /* tags from the EXIF ifd */
120 #define tag_exif_version 0x9000
121 #define tag_flashpix_version 0xA000
122 #define tag_color_space 0xA001
123 #define tag_component_configuration 0x9101
124 #define tag_component_bits_per_pixel 0x9102
125 #define tag_pixel_x_dimension 0xA002
126 #define tag_pixel_y_dimension 0xA003
127 #define tag_maker_note 0x927C
128 #define tag_user_comment 0x9286
129 #define tag_related_sound_file 0xA004
130 #define tag_date_time_original 0x9003
131 #define tag_date_time_digitized 0x9004
132 #define tag_sub_sec_time 0x9290
133 #define tag_sub_sec_time_original 0x9291
134 #define tag_sub_sec_time_digitized 0x9292
135 #define tag_image_unique_id 0xA420
136 #define tag_exposure_time 0x829a
137 #define tag_f_number 0x829D
138 #define tag_exposure_program 0x8822
139 #define tag_spectral_sensitivity 0x8824
140 #define tag_iso_speed_ratings 0x8827
141 #define tag_oecf 0x8828
142 #define tag_shutter_speed 0x9201
143 #define tag_aperture 0x9202
144 #define tag_brightness 0x9203
145 #define tag_exposure_bias 0x9204
146 #define tag_max_aperture 0x9205
147 #define tag_subject_distance 0x9206
148 #define tag_metering_mode 0x9207
149 #define tag_light_source 0x9208
150 #define tag_flash 0x9209
151 #define tag_focal_length 0x920a
152 #define tag_subject_area 0x9214
153 #define tag_flash_energy 0xA20B
154 #define tag_spatial_frequency_response 0xA20C
155 #define tag_focal_plane_x_resolution 0xA20e
156 #define tag_focal_plane_y_resolution 0xA20F
157 #define tag_focal_plane_resolution_unit 0xA210
158 #define tag_subject_location 0xA214
159 #define tag_exposure_index 0xA215
160 #define tag_sensing_method 0xA217
161 #define tag_file_source 0xA300
162 #define tag_scene_type 0xA301
163 #define tag_cfa_pattern 0xA302
164 #define tag_custom_rendered 0xA401
165 #define tag_exposure_mode 0xA402
166 #define tag_white_balance 0xA403
167 #define tag_digital_zoom_ratio 0xA404
168 #define tag_focal_length_in_35mm_film 0xA405
169 #define tag_scene_capture_type 0xA406
170 #define tag_gain_control 0xA407
171 #define tag_contrast 0xA408
172 #define tag_saturation 0xA409
173 #define tag_sharpness 0xA40A
174 #define tag_device_setting_description 0xA40B
175 #define tag_subject_distance_range 0xA40C
176
177 /* GPS tags */
178 #define tag_gps_version_id 0
179 #define tag_gps_latitude_ref 1
180 #define tag_gps_latitude 2
181 #define tag_gps_longitude_ref 3
182 #define tag_gps_longitude 4
183 #define tag_gps_altitude_ref 5
184 #define tag_gps_altitude 6
185 #define tag_gps_time_stamp 7
186 #define tag_gps_satellites 8
187 #define tag_gps_status 9
188 #define tag_gps_measure_mode 10
189 #define tag_gps_dop 11
190 #define tag_gps_speed_ref 12
191 #define tag_gps_speed 13
192 #define tag_gps_track_ref 14
193 #define tag_gps_track 15
194 #define tag_gps_img_direction_ref 16
195 #define tag_gps_img_direction 17
196 #define tag_gps_map_datum 18
197 #define tag_gps_dest_latitude_ref 19
198 #define tag_gps_dest_latitude 20
199 #define tag_gps_dest_longitude_ref 21
200 #define tag_gps_dest_longitude 22
201 #define tag_gps_dest_bearing_ref 23
202 #define tag_gps_dest_bearing 24
203 #define tag_gps_dest_distance_ref 25
204 #define tag_gps_dest_distance 26
205 #define tag_gps_processing_method 27
206 #define tag_gps_area_information 28
207 #define tag_gps_date_stamp 29
208 #define tag_gps_differential 30
209
210 /* don't use this on pointers */
211 #define ARRAY_COUNT(array) (sizeof(array)/sizeof(*array))
212
213 /* in memory tiff structure */
214 typedef struct {
215   /* the data we use as a tiff */
216   unsigned char *base;
217   size_t size;
218
219   /* intel or motorola byte order */
220   tiff_type type;
221
222   /* initial ifd offset */
223   unsigned long first_ifd_offset;
224   
225   /* size (in entries) and data */
226   int ifd_size; 
227   ifd_entry *ifd;
228   unsigned long next_ifd;
229 } imtiff;
230
231 static int tiff_init(imtiff *tiff, unsigned char *base, size_t length);
232 static int tiff_load_ifd(imtiff *tiff, unsigned long offset);
233 static void tiff_final(imtiff *tiff);
234 static void tiff_clear_ifd(imtiff *tiff);
235 #if 0 /* currently unused, but that may change */
236 static int tiff_get_bytes(imtiff *tiff, unsigned char *to, size_t offset, 
237                           size_t count);
238 #endif
239 static int tiff_get_tag_double(imtiff *, int index, double *result);
240 static int tiff_get_tag_int(imtiff *, int index, int *result);
241 static unsigned tiff_get16(imtiff *, unsigned long offset);
242 static unsigned tiff_get32(imtiff *, unsigned long offset);
243 static int tiff_get16s(imtiff *, unsigned long offset);
244 static int tiff_get32s(imtiff *, unsigned long offset);
245 static double tiff_get_rat(imtiff *, unsigned long offset);
246 static double tiff_get_rats(imtiff *, unsigned long offset);
247 static void save_ifd0_tags(i_img *im, imtiff *tiff, unsigned long *exif_ifd_offset, unsigned long *gps_ifd_offset);
248 static void save_exif_ifd_tags(i_img *im, imtiff *tiff);
249 static void save_gps_ifd_tags(i_img *im, imtiff *tiff);
250 static void
251 copy_string_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count);
252 static void
253 copy_int_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count);
254 static void
255 copy_rat_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count);
256 static void
257 copy_num_array_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count);
258 static void
259 copy_name_tags(i_img *im, imtiff *tiff, tag_value_map *map, int map_count);
260 static void process_maker_note(i_img *im, imtiff *tiff, unsigned long offset, size_t size);
261
262 /*
263 =head1 PUBLIC FUNCTIONS
264
265 These functions are available to other parts of Imager.  They aren't
266 intended to be called from outside of Imager.
267
268 =over
269
270 =item i_int_decode_exit
271
272 i_int_decode_exif(im, data_base, data_size);
273
274 The data from data_base for data_size bytes will be scanned for EXIF
275 data.
276
277 Any data found will be used to set tags in the supplied image.
278
279 The intent is that invalid EXIF data will simply fail to set tags, and
280 write to the log.  In no case should this code exit when supplied
281 invalid data.
282
283 Returns true if an Exif header was seen.
284
285 =cut
286 */
287
288 int
289 i_int_decode_exif(i_img *im, unsigned char *data, size_t length) {
290   imtiff tiff;
291   unsigned long exif_ifd_offset = 0;
292   unsigned long gps_ifd_offset = 0;
293   /* basic checks - must start with "Exif\0\0" */
294
295   if (length < 6 || memcmp(data, "Exif\0\0", 6) != 0) {
296     return 0;
297   }
298
299   data += 6;
300   length -= 6;
301
302   if (!tiff_init(&tiff, data, length)) {
303     mm_log((2, "Exif header found, but no valid TIFF header\n"));
304     return 1;
305   }
306   if (!tiff_load_ifd(&tiff, tiff.first_ifd_offset)) {
307     mm_log((2, "Exif header found, but could not load IFD 0\n"));
308     tiff_final(&tiff);
309     return 1;
310   }
311
312   save_ifd0_tags(im, &tiff, &exif_ifd_offset, &gps_ifd_offset);
313
314   if (exif_ifd_offset) {
315     if (tiff_load_ifd(&tiff, exif_ifd_offset)) {
316       save_exif_ifd_tags(im, &tiff);
317     }
318     else {
319       mm_log((2, "Could not load Exif IFD\n"));
320     }
321   }
322
323   if (gps_ifd_offset) {
324     if (tiff_load_ifd(&tiff, gps_ifd_offset)) {
325       save_gps_ifd_tags(im, &tiff);
326     }
327     else {
328       mm_log((2, "Could not load GPS IFD\n"));
329     }
330   }
331
332   tiff_final(&tiff);
333
334   return 1;
335 }
336
337 /*
338
339 =back
340
341 =head1 INTERNAL FUNCTIONS
342
343 =head2 EXIF Processing 
344
345 =over
346
347 =item save_ifd0_tags
348
349 save_ifd0_tags(im, tiff, &exif_ifd_offset, &gps_ifd_offset)
350
351 Scans the currently loaded IFD for tags expected in IFD0 and sets them
352 in the image.
353
354 Sets *exif_ifd_offset to the offset of the EXIF IFD if found.
355
356 =cut
357
358 */
359
360 static tag_map ifd0_string_tags[] =
361   {
362     { tag_make, "exif_make" },
363     { tag_model, "exif_model" },
364     { tag_copyright, "exif_copyright" },
365     { tag_software, "exif_software" },
366     { tag_artist, "exif_artist" },
367     { tag_date_time, "exif_date_time" },
368     { tag_image_description, "exif_image_description" },
369   };
370
371 static const int ifd0_string_tag_count = ARRAY_COUNT(ifd0_string_tags);
372
373 static tag_map ifd0_int_tags[] =
374   {
375     { tag_orientation, "exif_orientation", },
376     { tag_resolution_unit, "exif_resolution_unit" },
377   };
378
379 static const int ifd0_int_tag_count = ARRAY_COUNT(ifd0_int_tags);
380
381 static tag_map ifd0_rat_tags[] =
382   {
383     { tag_x_resolution, "exif_x_resolution" },
384     { tag_y_resolution, "exif_y_resolution" },
385   };
386
387 static tag_map resolution_unit_values[] =
388   {
389     { 1, "none" },
390     { 2, "inches" },
391     { 3, "centimeters" },
392   };
393
394 static tag_value_map ifd0_values[] =
395   {
396     VALUE_MAP_ENTRY(resolution_unit),
397   };
398
399 static void
400 save_ifd0_tags(i_img *im, imtiff *tiff, unsigned long *exif_ifd_offset,
401                unsigned long *gps_ifd_offset) {
402   int tag_index;
403   int work;
404   ifd_entry *entry;
405
406   for (tag_index = 0, entry = tiff->ifd; 
407        tag_index < tiff->ifd_size; ++tag_index, ++entry) {
408     switch (entry->tag) {
409     case tag_exif_ifd:
410       if (tiff_get_tag_int(tiff, tag_index, &work))
411         *exif_ifd_offset = work;
412       break;
413
414     case tag_gps_ifd:
415       if (tiff_get_tag_int(tiff, tag_index, &work))
416         *gps_ifd_offset = work;
417       break;
418     }
419   }
420
421   copy_string_tags(im, tiff, ifd0_string_tags, ifd0_string_tag_count);
422   copy_int_tags(im, tiff, ifd0_int_tags, ifd0_int_tag_count);
423   copy_rat_tags(im, tiff, ifd0_rat_tags, ARRAY_COUNT(ifd0_rat_tags));
424   copy_name_tags(im, tiff, ifd0_values, ARRAY_COUNT(ifd0_values));
425   /* copy_num_array_tags(im, tiff, ifd0_num_arrays, ARRAY_COUNT(ifd0_num_arrays)); */
426 }
427
428 /*
429 =item save_exif_ifd_tags
430
431 save_exif_ifd_tags(im, tiff)
432
433 Scans the currently loaded IFD for the tags expected in the EXIF IFD
434 and sets them as tags in the image.
435
436 =cut
437
438 */
439
440 static tag_map exif_ifd_string_tags[] =
441   {
442     { tag_exif_version, "exif_version", },
443     { tag_flashpix_version, "exif_flashpix_version", },
444     { tag_related_sound_file, "exif_related_sound_file", },
445     { tag_date_time_original, "exif_date_time_original", },
446     { tag_date_time_digitized, "exif_date_time_digitized", },
447     { tag_sub_sec_time, "exif_sub_sec_time" },
448     { tag_sub_sec_time_original, "exif_sub_sec_time_original" },
449     { tag_sub_sec_time_digitized, "exif_sub_sec_time_digitized" },
450     { tag_image_unique_id, "exif_image_unique_id" },
451     { tag_spectral_sensitivity, "exif_spectral_sensitivity" },
452   };
453
454 static const int exif_ifd_string_tag_count = ARRAY_COUNT(exif_ifd_string_tags);
455
456 static tag_map exif_ifd_int_tags[] =
457   {
458     { tag_color_space, "exif_color_space" },
459     { tag_exposure_program, "exif_exposure_program" },
460     { tag_metering_mode, "exif_metering_mode" },
461     { tag_light_source, "exif_light_source" },
462     { tag_flash, "exif_flash" },
463     { tag_focal_plane_resolution_unit, "exif_focal_plane_resolution_unit" },
464     { tag_subject_location, "exif_subject_location" },
465     { tag_sensing_method, "exif_sensing_method" },
466     { tag_custom_rendered, "exif_custom_rendered" },
467     { tag_exposure_mode, "exif_exposure_mode" },
468     { tag_white_balance, "exif_white_balance" },
469     { tag_focal_length_in_35mm_film, "exif_focal_length_in_35mm_film" },
470     { tag_scene_capture_type, "exif_scene_capture_type" },
471     { tag_contrast, "exif_contrast" },
472     { tag_saturation, "exif_saturation" },
473     { tag_sharpness, "exif_sharpness" },
474     { tag_subject_distance_range, "exif_subject_distance_range" },
475   };
476
477
478 static const int exif_ifd_int_tag_count = ARRAY_COUNT(exif_ifd_int_tags);
479
480 static tag_map exif_ifd_rat_tags[] =
481   {
482     { tag_exposure_time, "exif_exposure_time" },
483     { tag_f_number, "exif_f_number" },
484     { tag_shutter_speed, "exif_shutter_speed" },
485     { tag_aperture, "exif_aperture" },
486     { tag_brightness, "exif_brightness" },
487     { tag_exposure_bias, "exif_exposure_bias" },
488     { tag_max_aperture, "exif_max_aperture" },
489     { tag_subject_distance, "exif_subject_distance" },
490     { tag_focal_length, "exif_focal_length" },
491     { tag_flash_energy, "exif_flash_energy" },
492     { tag_focal_plane_x_resolution, "exif_focal_plane_x_resolution" },
493     { tag_focal_plane_y_resolution, "exif_focal_plane_y_resolution" },
494     { tag_exposure_index, "exif_exposure_index" },
495     { tag_digital_zoom_ratio, "exif_digital_zoom_ratio" },
496     { tag_gain_control, "exif_gain_control" },
497   };
498
499 static const int exif_ifd_rat_tag_count = ARRAY_COUNT(exif_ifd_rat_tags);
500
501 static tag_map exposure_mode_values[] =
502   {
503     { 0, "Auto exposure" },
504     { 1, "Manual exposure" },
505     { 2, "Auto bracket" },
506   };
507 static tag_map color_space_values[] =
508   {
509     { 1, "sRGB" },
510     { 0xFFFF, "Uncalibrated" },
511   };
512
513 static tag_map exposure_program_values[] =
514   {
515     { 0, "Not defined" },
516     { 1, "Manual" },
517     { 2, "Normal program" },
518     { 3, "Aperture priority" },
519     { 4, "Shutter priority" },
520     { 5, "Creative program" },
521     { 6, "Action program" },
522     { 7, "Portrait mode" },
523     { 8, "Landscape mode" },
524   };
525
526 static tag_map metering_mode_values[] =
527   {
528     { 0, "unknown" },
529     { 1, "Average" },
530     { 2, "CenterWeightedAverage" },
531     { 3, "Spot" },
532     { 4, "MultiSpot" },
533     { 5, "Pattern" },
534     { 6, "Partial" },
535     { 255, "other" },
536   };
537
538 static tag_map light_source_values[] =
539   {
540     { 0, "unknown" },
541     { 1, "Daylight" },
542     { 2, "Fluorescent" },
543     { 3, "Tungsten (incandescent light)" },
544     { 4, "Flash" },
545     { 9, "Fine weather" },
546     { 10, "Cloudy weather" },
547     { 11, "Shade" },
548     { 12, "Daylight fluorescent (D 5700 Ã 7100K)" },
549     { 13, "Day white fluorescent (N 4600 Ã 5400K)" },
550     { 14, "Cool white fluorescent (W 3900 Ã 4500K)" },
551     { 15, "White fluorescent (WW 3200 Ã 3700K)" },
552     { 17, "Standard light A" },
553     { 18, "Standard light B" },
554     { 19, "Standard light C" },
555     { 20, "D55" },
556     { 21, "D65" },
557     { 22, "D75" },
558     { 23, "D50" },
559     { 24, "ISO studio tungsten" },
560     { 255, "other light source" },
561   };
562
563 static tag_map flash_values[] =
564   {
565     { 0x0000, "Flash did not fire." },
566     { 0x0001, "Flash fired." },
567     { 0x0005, "Strobe return light not detected." },
568     { 0x0007, "Strobe return light detected." },
569     { 0x0009, "Flash fired, compulsory flash mode" },
570     { 0x000D, "Flash fired, compulsory flash mode, return light not detected" },
571     { 0x000F, "Flash fired, compulsory flash mode, return light detected" },
572     { 0x0010, "Flash did not fire, compulsory flash mode" },
573     { 0x0018, "Flash did not fire, auto mode" },
574     { 0x0019, "Flash fired, auto mode" },
575     { 0x001D, "Flash fired, auto mode, return light not detected" },
576     { 0x001F, "Flash fired, auto mode, return light detected" },
577     { 0x0020, "No flash function" },
578     { 0x0041, "Flash fired, red-eye reduction mode" },
579     { 0x0045, "Flash fired, red-eye reduction mode, return light not detected" },
580     { 0x0047, "Flash fired, red-eye reduction mode, return light detected" },
581     { 0x0049, "Flash fired, compulsory flash mode, red-eye reduction mode" },
582     { 0x004D, "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected" },
583     { 0x004F, "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected" },
584     { 0x0059, "Flash fired, auto mode, red-eye reduction mode" },
585     { 0x005D, "Flash fired, auto mode, return light not detected, red-eye reduction mode" },
586     { 0x005F, "Flash fired, auto mode, return light detected, red-eye reduction mode" },
587   };
588
589 static tag_map sensing_method_values[] =
590   {
591     { 1, "Not defined" },
592     { 2, "One-chip color area sensor" },
593     { 3, "Two-chip color area sensor" },
594     { 4, "Three-chip color area sensor" },
595     { 5, "Color sequential area sensor" },
596     { 7, "Trilinear sensor" },
597     { 8, "Color sequential linear sensor" },
598   };
599
600 static tag_map custom_rendered_values[] =
601   {
602     { 0, "Normal process" },
603     { 1, "Custom process" },
604   };
605
606 static tag_map white_balance_values[] =
607   {
608     { 0, "Auto white balance" },
609     { 1, "Manual white balance" },
610   };
611
612 static tag_map scene_capture_type_values[] =
613   {
614     { 0, "Standard" },
615     { 1, "Landscape" },
616     { 2, "Portrait" },
617     { 3, "Night scene" },
618   };
619
620 static tag_map gain_control_values[] =
621   {
622     { 0, "None" },
623     { 1, "Low gain up" },
624     { 2, "High gain up" },
625     { 3, "Low gain down" },
626     { 4, "High gain down" },
627   };
628
629 static tag_map contrast_values[] =
630   {
631     { 0, "Normal" },
632     { 1, "Soft" },
633     { 2, "Hard" },
634   };
635
636 static tag_map saturation_values[] =
637   {
638     { 0, "Normal" },
639     { 1, "Low saturation" },
640     { 2, "High saturation" },
641   };
642
643 static tag_map sharpness_values[] =
644   {
645     { 0, "Normal" },
646     { 1, "Soft" },
647     { 2, "Hard" },
648   };
649
650 static tag_map subject_distance_range_values[] =
651   {
652     { 0, "unknown" },
653     { 1, "Macro" },
654     { 2, "Close view" },
655     { 3, "Distant view" },
656   };
657
658 #define focal_plane_resolution_unit_values resolution_unit_values
659
660 static tag_value_map exif_ifd_values[] =
661   {
662     VALUE_MAP_ENTRY(exposure_mode),
663     VALUE_MAP_ENTRY(color_space),
664     VALUE_MAP_ENTRY(exposure_program),
665     VALUE_MAP_ENTRY(metering_mode),
666     VALUE_MAP_ENTRY(light_source),
667     VALUE_MAP_ENTRY(flash),
668     VALUE_MAP_ENTRY(sensing_method),
669     VALUE_MAP_ENTRY(custom_rendered),
670     VALUE_MAP_ENTRY(white_balance),
671     VALUE_MAP_ENTRY(scene_capture_type),
672     VALUE_MAP_ENTRY(gain_control),
673     VALUE_MAP_ENTRY(contrast),
674     VALUE_MAP_ENTRY(saturation),
675     VALUE_MAP_ENTRY(sharpness),
676     VALUE_MAP_ENTRY(subject_distance_range),
677     VALUE_MAP_ENTRY(focal_plane_resolution_unit),
678   };
679
680 static tag_map exif_num_arrays[] =
681   {
682     { tag_iso_speed_ratings, "exif_iso_speed_ratings" },
683     { tag_subject_area, "exif_subject_area" },
684     { tag_subject_location, "exif_subject_location" },
685   };
686
687 static void
688 save_exif_ifd_tags(i_img *im, imtiff *tiff) {
689   int i, tag_index;
690   ifd_entry *entry;
691   char *user_comment;
692   unsigned long maker_note_offset = 0;
693   size_t maker_note_size = 0;
694
695   for (tag_index = 0, entry = tiff->ifd; 
696        tag_index < tiff->ifd_size; ++tag_index, ++entry) {
697     switch (entry->tag) {
698     case tag_user_comment:
699       /* I don't want to trash the source, so work on a copy */
700       user_comment = mymalloc(entry->size);
701       memcpy(user_comment, tiff->base + entry->offset, entry->size);
702       /* the first 8 bytes indicate the encoding, make them into spaces
703          for better presentation */
704       for (i = 0; i < entry->size && i < 8; ++i) {
705         if (user_comment[i] == '\0')
706           user_comment[i] = ' ';
707       }
708       /* find the actual end of the string */
709       while (i < entry->size && user_comment[i])
710         ++i;
711       i_tags_set(&im->tags, "exif_user_comment", user_comment, i);
712       myfree(user_comment);
713       break;
714
715     case tag_maker_note:
716       maker_note_offset = entry->offset;
717       maker_note_size = entry->size;
718       break;
719
720       /* the following aren't processed yet */
721     case tag_oecf:
722     case tag_spatial_frequency_response:
723     case tag_file_source:
724     case tag_scene_type:
725     case tag_cfa_pattern:
726     case tag_device_setting_description:
727     case tag_subject_area:
728       break;
729     }
730   }
731
732   copy_string_tags(im, tiff, exif_ifd_string_tags, exif_ifd_string_tag_count);
733   copy_int_tags(im, tiff, exif_ifd_int_tags, exif_ifd_int_tag_count);
734   copy_rat_tags(im, tiff, exif_ifd_rat_tags, exif_ifd_rat_tag_count);
735   copy_name_tags(im, tiff, exif_ifd_values, ARRAY_COUNT(exif_ifd_values));
736   copy_num_array_tags(im, tiff, exif_num_arrays, ARRAY_COUNT(exif_num_arrays));
737
738   /* This trashes the IFD - make sure it's done last */
739   if (maker_note_offset) {
740     process_maker_note(im, tiff, maker_note_offset, maker_note_size);
741   }
742 }
743
744 static tag_map gps_ifd_string_tags[] =
745   {
746     { tag_gps_version_id, "exif_gps_version_id" },
747     { tag_gps_latitude_ref, "exif_gps_latitude_ref" },
748     { tag_gps_longitude_ref, "exif_gps_longitude_ref" },
749     { tag_gps_altitude_ref, "exif_gps_altitude_ref" },
750     { tag_gps_satellites, "exif_gps_satellites" },
751     { tag_gps_status, "exif_gps_status" },
752     { tag_gps_measure_mode, "exif_gps_measure_mode" },
753     { tag_gps_speed_ref, "exif_gps_speed_ref" },
754     { tag_gps_track_ref, "exif_gps_track_ref" },
755   };
756
757 static tag_map gps_ifd_int_tags[] =
758   {
759     { tag_gps_differential, "exif_gps_differential" },
760   };
761
762 static tag_map gps_ifd_rat_tags[] =
763   {
764     { tag_gps_altitude, "exif_gps_altitude" },
765     { tag_gps_time_stamp, "exif_gps_time_stamp" },
766     { tag_gps_dop, "exif_gps_dop" },
767     { tag_gps_speed, "exif_gps_speed" },
768     { tag_gps_track, "exif_track" }
769   };
770
771 static tag_map gps_differential_values [] =
772   {
773     { 0, "without differential correction" },
774     { 1, "Differential correction applied" },
775   };
776
777 static tag_value_map gps_ifd_values[] =
778   {
779     VALUE_MAP_ENTRY(gps_differential),
780   };
781
782 static tag_map gps_num_arrays[] =
783   {
784     { tag_gps_latitude, "exif_gps_latitude" },
785     { tag_gps_longitude, "exif_gps_longitude" },
786   };
787
788 static void
789 save_gps_ifd_tags(i_img *im, imtiff *tiff) {
790   /* int i, tag_index; 
791   int work;
792   ifd_entry *entry; */
793
794   /* for (tag_index = 0, entry = tiff->ifd; 
795        tag_index < tiff->ifd_size; ++tag_index, ++entry) {
796     switch (entry->tag) {
797       break;
798     }
799     }*/
800
801   copy_string_tags(im, tiff, gps_ifd_string_tags, 
802          ARRAY_COUNT(gps_ifd_string_tags));
803   copy_int_tags(im, tiff, gps_ifd_int_tags, ARRAY_COUNT(gps_ifd_int_tags));
804   copy_rat_tags(im, tiff, gps_ifd_rat_tags, ARRAY_COUNT(gps_ifd_rat_tags));
805   copy_name_tags(im, tiff, gps_ifd_values, ARRAY_COUNT(gps_ifd_values));
806   copy_num_array_tags(im, tiff, gps_num_arrays, ARRAY_COUNT(gps_num_arrays));
807 }
808
809 /*
810 =item process_maker_note
811
812 This is a stub for processing the maker note tag.
813
814 Maker notes aren't covered by EXIF itself and in general aren't
815 documented by the manufacturers.
816
817 =cut
818 */
819
820 static void
821 process_maker_note(i_img *im, imtiff *tiff, unsigned long offset, size_t size) {
822   /* this will be added in a future release */
823 }
824
825 /*
826 =back
827
828 =head2 High level TIFF functions
829
830 To avoid relying upon tifflib when we're not processing an image we
831 have some simple in-memory TIFF file management.
832
833 =over
834
835 =item tiff_init
836
837 imtiff tiff;
838 if (tiff_init(tiff, data_base, data_size)) {
839   // success
840 }
841
842 Initialize the tiff data structure.
843
844 Scans for the byte order and version markers, and stores the offset to
845 the first IFD (IFD0 in EXIF) in first_ifd_offset.
846
847 =cut
848 */
849
850 static int
851 tiff_init(imtiff *tiff, unsigned char *data, size_t length) {
852   int version;
853
854   tiff->base = data;
855   tiff->size = length;
856   if (length < 8) /* well... would have to be much bigger to be useful */
857     return 0;
858   if (data[0] == 'M' && data[1] == 'M')
859     tiff->type = tt_motorola;
860   else if (data[0] == 'I' && data[1] == 'I') 
861     tiff->type = tt_intel;
862   else
863     return 0; /* invalid header */
864
865   version = tiff_get16(tiff, 2);
866   if (version != 42)
867     return 0;
868
869   tiff->first_ifd_offset = tiff_get32(tiff, 4);
870   if (tiff->first_ifd_offset > length || tiff->first_ifd_offset < 8)
871     return 0;
872
873   tiff->ifd_size = 0;
874   tiff->ifd = NULL;
875   tiff->next_ifd = 0;
876
877   return 1;
878 }
879
880 /*
881 =item tiff_final
882
883 tiff_final(&tiff)
884
885 Clean up the tiff structure initialized by tiff_init()
886
887 =cut
888 */
889
890 static void
891 tiff_final(imtiff *tiff) {
892   tiff_clear_ifd(tiff);
893 }
894
895 /*
896 =item tiff_load_ifd
897
898 if (tiff_load_ifd(tiff, offset)) {
899   // process the ifd
900 }
901
902 Loads the IFD from the given offset into the tiff objects ifd.
903
904 This can fail if the IFD extends beyond end of file, or if any data
905 offsets combined with their sizes, extends beyond end of file.
906
907 Returns true on success.
908
909 =cut
910 */
911
912 static int
913 tiff_load_ifd(imtiff *tiff, unsigned long offset) {
914   unsigned count;
915   int ifd_size;
916   ifd_entry *entries = NULL;
917   int i;
918   unsigned long base;
919
920   tiff_clear_ifd(tiff);
921
922   /* rough check count + 1 entry + next offset */
923   if (offset + (2+12+4) > tiff->size) {
924     mm_log((2, "offset %lu beyond end off Exif block", offset));
925     return 0;
926   }
927
928   count = tiff_get16(tiff, offset);
929   
930   /* check we can fit the whole thing */
931   ifd_size = 2 + count * 12 + 4; /* count + count entries + next offset */
932   if (offset + ifd_size > tiff->size) {
933     mm_log((2, "offset %lu beyond end off Exif block", offset));
934     return 0;
935   }
936
937   entries = mymalloc(count * sizeof(ifd_entry));
938   memset(entries, 0, count * sizeof(ifd_entry));
939   base = offset + 2;
940   for (i = 0; i < count; ++i) {
941     ifd_entry *entry = entries + i;
942     entry->tag = tiff_get16(tiff, base);
943     entry->type = tiff_get16(tiff, base+2);
944     entry->count = tiff_get32(tiff, base+4);
945     if (entry->type >= 1 && entry->type <= ift_last) {
946       entry->item_size = type_sizes[entry->type];
947       entry->size = entry->item_size * entry->count;
948       if (entry->size / entry->item_size != entry->count) {
949         myfree(entries);
950         mm_log((1, "Integer overflow calculating tag data size processing EXIF block\n"));
951         return 0;
952       }
953       else if (entry->size <= 4) {
954         entry->offset = base + 8;
955       }
956       else {
957         entry->offset = tiff_get32(tiff, base+8);
958         if (entry->offset + entry->size > tiff->size) {
959           mm_log((2, "Invalid data offset processing IFD\n"));
960           myfree(entries);
961           return 0;
962         }
963       }
964     }
965     else {
966       entry->size = 0;
967       entry->offset = 0;
968     }
969     base += 12;
970   }
971
972   tiff->ifd_size = count;
973   tiff->ifd = entries;
974   tiff->next_ifd = tiff_get32(tiff, base);
975
976   return 1;
977 }
978
979 /*
980 =item tiff_clear_ifd
981
982 tiff_clear_ifd(tiff)
983
984 Releases any memory associated with the stored IFD and resets the IFD
985 pointers.
986
987 This is called by tiff_load_ifd() and tiff_final().
988
989 =cut
990 */
991
992 static void
993 tiff_clear_ifd(imtiff *tiff) {
994   if (tiff->ifd_size && tiff->ifd) {
995     myfree(tiff->ifd);
996     tiff->ifd_size = 0;
997     tiff->ifd = NULL;
998   }
999 }
1000
1001 /*
1002 =item tiff_get_tag_double
1003
1004   double value;
1005   if (tiff_get_tag(tiff, index, &value)) {
1006     // process value
1007   }
1008
1009 Attempts to retrieve a double value from the given index in the
1010 current IFD.
1011
1012 The value must have a count of 1.
1013
1014 =cut
1015 */
1016
1017 static int
1018 tiff_get_tag_double_array(imtiff *tiff, int index, double *result, 
1019                           int array_index) {
1020   ifd_entry *entry;
1021   unsigned long offset;
1022   if (index < 0 || index >= tiff->ifd_size) {
1023     mm_log((3, "tiff_get_tag_double_array() tag index out of range"));
1024     return 0;
1025   }
1026   
1027   entry = tiff->ifd + index;
1028   if (array_index < 0 || array_index >= entry->count) {
1029     mm_log((3, "tiff_get_tag_double_array() array index out of range"));
1030     return 0;
1031   }
1032
1033   offset = entry->offset + array_index * entry->item_size;
1034
1035   switch (entry->type) {
1036   case ift_short:
1037     *result = tiff_get16(tiff, offset);
1038     return 1;
1039    
1040   case ift_long:
1041     *result = tiff_get32(tiff, offset);
1042     return 1;
1043
1044   case ift_rational:
1045     *result = tiff_get_rat(tiff, offset);
1046     return 1;
1047
1048   case ift_sshort:
1049     *result = tiff_get16s(tiff, offset);
1050     return 1;
1051
1052   case ift_slong:
1053     *result = tiff_get32s(tiff, offset);
1054     return 1;
1055
1056   case ift_srational:
1057     *result = tiff_get_rats(tiff, offset);
1058     return 1;
1059
1060   case ift_byte:
1061     *result = *(tiff->base + offset);
1062     return 1;
1063   }
1064
1065   return 0;
1066 }
1067
1068 /*
1069 =item tiff_get_tag_double
1070
1071   double value;
1072   if (tiff_get_tag(tiff, index, &value)) {
1073     // process value
1074   }
1075
1076 Attempts to retrieve a double value from the given index in the
1077 current IFD.
1078
1079 The value must have a count of 1.
1080
1081 =cut
1082 */
1083
1084 static int
1085 tiff_get_tag_double(imtiff *tiff, int index, double *result) {
1086   ifd_entry *entry;
1087   if (index < 0 || index >= tiff->ifd_size) {
1088     mm_log((3, "tiff_get_tag_double() index out of range"));
1089     return 0;
1090   }
1091   
1092   entry = tiff->ifd + index;
1093   if (entry->count != 1) {
1094     mm_log((3, "tiff_get_tag_double() called on tag with multiple values"));
1095     return 0;
1096   }
1097
1098   return tiff_get_tag_double_array(tiff, index, result, 0);
1099 }
1100
1101 /*
1102 =item tiff_get_tag_int_array
1103
1104   int value;
1105   if (tiff_get_tag_int_array(tiff, index, &value, array_index)) {
1106     // process value
1107   }
1108
1109 Attempts to retrieve an integer value from the given index in the
1110 current IFD.
1111
1112 =cut
1113 */
1114
1115 static int
1116 tiff_get_tag_int_array(imtiff *tiff, int index, int *result, int array_index) {
1117   ifd_entry *entry;
1118   unsigned long offset;
1119   if (index < 0 || index >= tiff->ifd_size) {
1120     mm_log((3, "tiff_get_tag_int_array() tag index out of range"));
1121     return 0;
1122   }
1123   
1124   entry = tiff->ifd + index;
1125   if (array_index < 0 || array_index >= entry->count) {
1126     mm_log((3, "tiff_get_tag_int_array() array index out of range"));
1127     return 0;
1128   }
1129
1130   offset = entry->offset + array_index * entry->item_size;
1131
1132   switch (entry->type) {
1133   case ift_short:
1134     *result = tiff_get16(tiff, offset);
1135     return 1;
1136    
1137   case ift_long:
1138     *result = tiff_get32(tiff, offset);
1139     return 1;
1140
1141   case ift_sshort:
1142     *result = tiff_get16s(tiff, offset);
1143     return 1;
1144
1145   case ift_slong:
1146     *result = tiff_get32s(tiff, offset);
1147     return 1;
1148
1149   case ift_byte:
1150     *result = *(tiff->base + offset);
1151     return 1;
1152   }
1153
1154   return 0;
1155 }
1156
1157 /*
1158 =item tiff_get_tag_int
1159
1160   int value;
1161   if (tiff_get_tag_int(tiff, index, &value)) {
1162     // process value
1163   }
1164
1165 Attempts to retrieve an integer value from the given index in the
1166 current IFD.
1167
1168 The value must have a count of 1.
1169
1170 =cut
1171 */
1172
1173 static int
1174 tiff_get_tag_int(imtiff *tiff, int index, int *result) {
1175   ifd_entry *entry;
1176   if (index < 0 || index >= tiff->ifd_size) {
1177     mm_log((3, "tiff_get_tag_int() index out of range"));
1178     return 0;
1179   }
1180
1181   entry = tiff->ifd + index;
1182   if (entry->count != 1) {
1183     mm_log((3, "tiff_get_tag_int() called on tag with multiple values"));
1184     return 0;
1185   }
1186
1187   return tiff_get_tag_int_array(tiff, index, result, 0);
1188 }
1189
1190 /*
1191 =back
1192
1193 =head2 Table-based tag setters
1194
1195 This set of functions checks for matches between the current IFD and
1196 tags supplied in an array, when there's a match it sets the
1197 appropriate tag in the image.
1198
1199 =over
1200
1201 =item copy_int_tags
1202
1203 Scans the IFD for integer tags and sets them in the image,
1204
1205 =cut
1206 */
1207
1208 static void
1209 copy_int_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count) {
1210   int i, tag_index;
1211   ifd_entry *entry;
1212
1213   for (tag_index = 0, entry = tiff->ifd; 
1214        tag_index < tiff->ifd_size; ++tag_index, ++entry) {
1215     for (i = 0; i < map_count; ++i) {
1216       int value;
1217       if (map[i].tag == entry->tag
1218           && tiff_get_tag_int(tiff, tag_index, &value)) {
1219         i_tags_setn(&im->tags, map[i].name, value);
1220         break;
1221       }
1222     }
1223   }
1224 }
1225
1226 /*
1227 =item copy_rat_tags
1228
1229 Scans the IFD for rational tags and sets them in the image.
1230
1231 =cut
1232 */
1233
1234 static void
1235 copy_rat_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count) {
1236   int i, tag_index;
1237   ifd_entry *entry;
1238
1239   for (tag_index = 0, entry = tiff->ifd; 
1240        tag_index < tiff->ifd_size; ++tag_index, ++entry) {
1241     for (i = 0; i < map_count; ++i) {
1242       double value;
1243       if (map[i].tag == entry->tag
1244           && tiff_get_tag_double(tiff, tag_index, &value)) {
1245         i_tags_set_float2(&im->tags, map[i].name, 0, value, 6);
1246         break;
1247       }
1248     }
1249   }
1250 }
1251
1252 /*
1253 =item copy_string_tags
1254
1255 Scans the IFD for string tags and sets them in the image.
1256
1257 =cut
1258 */
1259
1260 static void
1261 copy_string_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count) {
1262   int i, tag_index;
1263   ifd_entry *entry;
1264
1265   for (tag_index = 0, entry = tiff->ifd; 
1266        tag_index < tiff->ifd_size; ++tag_index, ++entry) {
1267     for (i = 0; i < map_count; ++i) {
1268       if (map[i].tag == entry->tag) {
1269         int len = entry->type == ift_ascii ? entry->size - 1 : entry->size;
1270         i_tags_set(&im->tags, map[i].name,
1271                    (char const *)(tiff->base + entry->offset), len);
1272         break;
1273       }
1274     }
1275   }
1276 }
1277
1278 /*
1279 =item copy_num_array_tags
1280
1281 Scans the IFD for arrays of numbers and sets them in the image.
1282
1283 =cut
1284 */
1285
1286 /* a more general solution would be better in some ways, but we don't need it */
1287 #define MAX_ARRAY_VALUES 10
1288 #define MAX_ARRAY_STRING (MAX_ARRAY_VALUES * 20)
1289
1290 static void
1291 copy_num_array_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count) {
1292   int i, j, tag_index;
1293   ifd_entry *entry;
1294
1295   for (tag_index = 0, entry = tiff->ifd; 
1296        tag_index < tiff->ifd_size; ++tag_index, ++entry) {
1297     for (i = 0; i < map_count; ++i) {
1298       if (map[i].tag == entry->tag && entry->count <= MAX_ARRAY_VALUES) {
1299         if (entry->type == ift_rational || entry->type == ift_srational) {
1300           double value;
1301           char workstr[MAX_ARRAY_STRING];
1302           size_t len = 0, item_len;
1303           *workstr = '\0';
1304           for (j = 0; j < entry->count; ++j) {
1305             if (!tiff_get_tag_double_array(tiff, tag_index, &value, j)) {
1306               mm_log((3, "unexpected failure from tiff_get_tag_double_array(..., %d, ..., %d)\n", tag_index, j));
1307               return;
1308             }
1309             if (len >= sizeof(workstr) - 1) {
1310               mm_log((3, "Buffer would overflow reading tag %#x\n", entry->tag));
1311               return;
1312             }
1313             if (j) {
1314               strcat(workstr, " ");
1315               ++len;
1316             }
1317 #ifdef IMAGER_SNPRINTF
1318             item_len = snprintf(workstr + len, sizeof(workstr)-len, "%.6g", value);
1319 #else
1320             item_len = sprintf(workstr + len, "%.6g", value);
1321 #endif
1322             len += item_len;
1323           }
1324           i_tags_set(&im->tags, map[i].name, workstr, -1);
1325         }
1326         else if (entry->type == ift_short || entry->type == ift_long
1327                  || entry->type == ift_sshort || entry->type == ift_slong
1328                  || entry->type == ift_byte) {
1329           int value;
1330           char workstr[MAX_ARRAY_STRING];
1331           size_t len = 0, item_len;
1332           *workstr = '\0';
1333           for (j = 0; j < entry->count; ++j) {
1334             if (!tiff_get_tag_int_array(tiff, tag_index, &value, j)) {
1335               mm_log((3, "unexpected failure from tiff_get_tag_int_array(..., %d, ..., %d)\n", tag_index, j));
1336               return;
1337             }
1338             if (len >= sizeof(workstr) - 1) {
1339               mm_log((3, "Buffer would overflow reading tag %#x\n", entry->tag));
1340               return;
1341             }
1342             if (j) {
1343               strcat(workstr, " ");
1344               ++len;
1345             }
1346 #ifdef IMAGER_SNPRINTF
1347             item_len = snprintf(workstr + len, sizeof(workstr) - len, "%d", value);
1348 #else
1349             item_len = sprintf(workstr + len, "%d", value);
1350 #endif
1351             len += item_len;
1352           }
1353           i_tags_set(&im->tags, map[i].name, workstr, -1);
1354         }
1355         break;
1356       }
1357     }
1358   }
1359 }
1360
1361 /*
1362 =item copy_name_tags
1363
1364 This function maps integer values to descriptions for those values.
1365
1366 In general we handle the integer value through copy_int_tags() and
1367 then the same tage with a "_name" suffix here.
1368
1369 =cut
1370 */
1371
1372 static void
1373 copy_name_tags(i_img *im, imtiff *tiff, tag_value_map *map, int map_count) {
1374   int i, j, tag_index;
1375   ifd_entry *entry;
1376
1377   for (tag_index = 0, entry = tiff->ifd; 
1378        tag_index < tiff->ifd_size; ++tag_index, ++entry) {
1379     for (i = 0; i < map_count; ++i) {
1380       int value;
1381       if (map[i].tag == entry->tag
1382           && tiff_get_tag_int(tiff, tag_index, &value)) {
1383         tag_map const *found = NULL;
1384         for (j = 0; j < map[i].map_count; ++j) {
1385           if (value == map[i].map[j].tag) {
1386             found = map[i].map + j;
1387             break;
1388           }
1389         }
1390         if (found) {
1391           i_tags_set(&im->tags, map[i].name, found->name, -1);
1392         }
1393         break;
1394       }
1395     }
1396   }
1397 }
1398
1399
1400 /*
1401 =back
1402
1403 =head2 Low level data access functions
1404
1405 These functions use the byte order in the tiff object to extract
1406 various types of data from the tiff data.
1407
1408 These functions will abort if called with an out of range offset.
1409
1410 The intent is that any offset checks should have been done by the caller.
1411
1412 =over
1413
1414 =item tiff_get16
1415
1416 Retrieve a 16 bit unsigned integer from offset.
1417
1418 =cut
1419 */
1420
1421 static unsigned
1422 tiff_get16(imtiff *tiff, unsigned long offset) {
1423   if (offset + 2 > tiff->size) {
1424     mm_log((3, "attempt to get16 at %lu in %lu image", offset,
1425             (unsigned long)tiff->size));
1426     return 0;
1427   }
1428
1429   if (tiff->type == tt_intel) 
1430     return tiff->base[offset] + 0x100 * tiff->base[offset+1];
1431   else
1432     return tiff->base[offset+1] + 0x100 * tiff->base[offset];
1433 }
1434
1435 /*
1436 =item tiff_get32
1437
1438 Retrieve a 32-bit unsigned integer from offset.
1439
1440 =cut
1441 */
1442
1443 static unsigned
1444 tiff_get32(imtiff *tiff, unsigned long offset) {
1445   if (offset + 4 > tiff->size) {
1446     mm_log((3, "attempt to get16 at %lu in %lu image", offset,
1447             (unsigned long)tiff->size));
1448     return 0;
1449   }
1450
1451   if (tiff->type == tt_intel) 
1452     return tiff->base[offset] + 0x100 * tiff->base[offset+1] 
1453       + 0x10000 * tiff->base[offset+2] + 0x1000000 * tiff->base[offset+3];
1454   else
1455     return tiff->base[offset+3] + 0x100 * tiff->base[offset+2]
1456       + 0x10000 * tiff->base[offset+1] + 0x1000000 * tiff->base[offset];
1457 }
1458
1459 #if 0 /* currently unused, but that may change */
1460
1461 /*
1462 =item tiff_get_bytes
1463
1464 Retrieve a byte string from offset.
1465
1466 This isn't used much, you can usually deal with the data in-situ.
1467 This is intended for use when you need to modify the data in some way.
1468
1469 =cut
1470 */
1471
1472 static int
1473 tiff_get_bytes(imtiff *tiff, unsigned char *data, size_t offset, 
1474                size_t size) {
1475   if (offset + size > tiff->size)
1476     return 0;
1477
1478   memcpy(data, tiff->base+offset, size);
1479
1480   return 1;
1481 }
1482
1483 #endif
1484
1485 /*
1486 =item tiff_get16s
1487
1488 Retrieve a 16-bit signed integer from offset.
1489
1490 =cut
1491 */
1492
1493 static int
1494 tiff_get16s(imtiff *tiff, unsigned long offset) {
1495   int result;
1496
1497   if (offset + 2 > tiff->size) {
1498     mm_log((3, "attempt to get16 at %lu in %lu image", offset,
1499             (unsigned long)tiff->size));
1500     return 0;
1501   }
1502
1503   if (tiff->type == tt_intel) 
1504     result = tiff->base[offset] + 0x100 * tiff->base[offset+1];
1505   else
1506     result = tiff->base[offset+1] + 0x100 * tiff->base[offset];
1507
1508   if (result > 0x7FFF)
1509     result -= 0x10000;
1510
1511   return result;
1512 }
1513
1514 /*
1515 =item tiff_get32s
1516
1517 Retrieve a 32-bit signed integer from offset.
1518
1519 =cut
1520 */
1521
1522 static int
1523 tiff_get32s(imtiff *tiff, unsigned long offset) {
1524   unsigned work;
1525
1526   if (offset + 4 > tiff->size) {
1527     mm_log((3, "attempt to get16 at %lu in %lu image", offset,
1528             (unsigned long)tiff->size));
1529     return 0;
1530   }
1531
1532   if (tiff->type == tt_intel) 
1533     work = tiff->base[offset] + 0x100 * tiff->base[offset+1] 
1534       + 0x10000 * tiff->base[offset+2] + 0x1000000 * tiff->base[offset+3];
1535   else
1536     work = tiff->base[offset+3] + 0x100 * tiff->base[offset+2]
1537       + 0x10000 * tiff->base[offset+1] + 0x1000000 * tiff->base[offset];
1538
1539   /* not really needed on 32-bit int machines */
1540   if (work > 0x7FFFFFFFUL)
1541     return work - 0x80000000UL;
1542   else
1543     return work;
1544 }
1545
1546 /*
1547 =item tiff_get_rat
1548
1549 Retrieve an unsigned rational from offset.
1550
1551 =cut
1552 */
1553
1554 static double
1555 tiff_get_rat(imtiff *tiff, unsigned long offset) {
1556   unsigned long numer, denom;
1557   if (offset + 8 > tiff->size) {
1558     mm_log((3, "attempt to get_rat at %lu in %lu image", offset,
1559             (unsigned long)tiff->size));
1560     return 0;
1561   }
1562
1563   numer = tiff_get32(tiff, offset);
1564   denom = tiff_get32(tiff, offset+4);
1565
1566   if (denom == 0) {
1567     return -DBL_MAX;
1568   }
1569
1570   return (double)numer / denom;
1571 }
1572
1573 /*
1574 =item tiff_get_rats
1575
1576 Retrieve an signed rational from offset.
1577
1578 =cut
1579 */
1580
1581 static double
1582 tiff_get_rats(imtiff *tiff, unsigned long offset) {
1583   long numer, denom;
1584   if (offset + 8 > tiff->size) {
1585     mm_log((3, "attempt to get_rat at %lu in %lu image", offset,
1586             (unsigned long)tiff->size));
1587     return 0;
1588   }
1589
1590   numer = tiff_get32s(tiff, offset);
1591   denom = tiff_get32s(tiff, offset+4);
1592
1593   if (denom == 0) {
1594     return -DBL_MAX;
1595   }
1596
1597   return (double)numer / denom;
1598 }
1599
1600 /*
1601 =back
1602
1603 =head1 SEE ALSO
1604
1605 L<Imager>, jpeg.c
1606
1607 http://www.exif.org/
1608
1609 =head1 AUTHOR
1610
1611 Tony Cook <tonyc@cpan.org>
1612
1613 =head1 REVISION
1614
1615 $Revision$
1616
1617 =cut
1618 */