exif_utility 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a170d2a17362fee9fc97611b715544a2fc282f9f
4
+ data.tar.gz: 7f80ff385be3436068dcc9f08ac08fd2b85124e8
5
+ SHA512:
6
+ metadata.gz: 45b1ac562b6cc886d87e831c23f561590467880052abd5739d9de5fc0d744338e124fd9ae2e245a40aa6100a1e915777413ca7ccc15e74ffc289e0bff53c46b3
7
+ data.tar.gz: 6931c287d71a66811b64d351f2410f07602bca2bfd02902584de0d7025cf25b8f1c2b02337eb7088039cd83cbef011f46836b3622c129e55343b563e364a14f4
data/bin/exif_utility ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'exif_utility'
4
+ puts ExifUtility.hi(ARGV[0])
@@ -0,0 +1,20 @@
1
+ module ExifUtility
2
+ class ExifUtility
3
+
4
+ # Exif data is stored in the Data class
5
+ attr_accessor :data
6
+
7
+ def initialize(filename)
8
+ @data = Data.new
9
+ Parser.new(filename, data)
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+
16
+ require 'date'
17
+
18
+ require 'exif_utility/data'
19
+ require 'exif_utility/parser'
20
+ require 'exif_utility/interpreter'
@@ -0,0 +1,26 @@
1
+ module ExifUtility
2
+ # Contains the values for each tag, populated by the parser
3
+ class Data
4
+
5
+ # TODO: Save data read in for each tag
6
+
7
+
8
+ # These are the tags, in order. Blank lines means tags were skipped.
9
+ attr_accessor :make # 0x010f
10
+ attr_accessor :model # 0x0110
11
+
12
+ attr_accessor :software # 0x0131
13
+ attr_accessor :modify_date # 0x0132
14
+
15
+ attr_accessor :exif_offset # 0x876
16
+
17
+ attr_accessor :date_time_original # 0x9003
18
+ attr_accessor :create_date # 0x9004
19
+
20
+
21
+ def initialize
22
+ # nothing needed to be done currently
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,918 @@
1
+ module ExifUtility
2
+ class Interpreter
3
+
4
+ def initialize(data)
5
+ @data = data
6
+
7
+ # data_format =>
8
+ @data_format_and_size = {
9
+ "0001" => 1, # unsigned byte
10
+ "0002" => 1, # ascii character
11
+ "0003" => 2, # unsigned short
12
+ "0004" => 4, # unsigned long
13
+ "0005" => 8, # unsigned rational: a fractional value containing 2 signed/unsigned long integer values: the first is the numerator, the second is the denominator
14
+ "0006" => 1, # signed byte
15
+ "0007" => 1, # undefined
16
+ "0008" => 2, # signed short
17
+ "0009" => 4, # signed long
18
+ "000A" => 8, # signed rational
19
+ "000B" => 4, # single float
20
+ "000C" => 8 # double float
21
+ }
22
+ end
23
+
24
+ def size_from_data_format(data_format)
25
+ @data_format_and_size[data_format]
26
+ end
27
+
28
+
29
+
30
+
31
+ def interpret_as_string(data)
32
+ # Also removes the null character at the end.
33
+ data.scan(/../).map(&:hex).map(&:chr).join[0..-2]
34
+ end
35
+
36
+ def interpret_as_number(data)
37
+ data.to_i(16)
38
+ end
39
+
40
+ def interpret_as_date_time(data)
41
+ data = interpret_as_string(data)
42
+ DateTime.strptime(data, '%Y:%m:%d %H:%M:%S')
43
+ end
44
+
45
+
46
+
47
+ # http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
48
+ def interpret(tag, data)
49
+ case tag
50
+
51
+
52
+ # when "0001"
53
+ # @data.interop_index = data
54
+ # when "0002"
55
+ # @data.interop_version = data
56
+ # when "000B"
57
+ # @data.processing_software = data
58
+ # when "00FE"
59
+ # @data.subfile_type = data
60
+ # when "00FF"
61
+ # @data.old_subfile_type = data
62
+ # when "0100"
63
+ # @data.image_width = data
64
+ # when "0101"
65
+ # @data.image_height = data
66
+ # when "0102"
67
+ # @data.bits_per_sample = data
68
+ # when "0103"
69
+ # @data.compression = data
70
+ # when "0106"
71
+ # @data.photometric_interpretation = data
72
+ # when "0107"
73
+ # @data.thresholding = data
74
+ # when "0108"
75
+ # @data.cell_width = data
76
+ # when "0109"
77
+ # @data.cell_length = data
78
+ # when "010A"
79
+ # @data.fill_order = data
80
+ # when "010D"
81
+ # @data.document_name = data
82
+ # when "010E"
83
+ # @data.image_description = data
84
+ when "010F"
85
+ @data.make = interpret_as_string(data)
86
+ when "0110"
87
+ @data.model = interpret_as_string(data)
88
+ # when "0111"
89
+ # @data.strip_offsets = data
90
+ # when "0112"
91
+ # @data.orientation = data
92
+ # when "0115"
93
+ # @data.samples_per_pixel = data
94
+ # when "0116"
95
+ # @data.rows_per_strip = data
96
+ # when "0117"
97
+ # @data.strip_byte_counts = data
98
+ # when "0118"
99
+ # @data.min_sample_value = data
100
+ # when "0119"
101
+ # @data.max_sample_value = data
102
+ # when "011A"
103
+ # @data.x_resolution = data
104
+ # when "011B"
105
+ # @data.y_resolution = data
106
+ # when "011C"
107
+ # @data.planar_configuration = data
108
+ # when "011D"
109
+ # @data.page_name = data
110
+ # when "011E"
111
+ # @data.x_position = data
112
+ # when "011F"
113
+ # @data.y_position = data
114
+ # when "0120"
115
+ # @data.free_offsets = data
116
+ # when "0121"
117
+ # @data.free_byte_counts = data
118
+ # when "0122"
119
+ # @data.gray_response_unit = data
120
+ # when "0123"
121
+ # @data.gray_response_curve = data
122
+ # when "0124"
123
+ # @data.t4_options = data
124
+ # when "0125"
125
+ # @data.t6_options = data
126
+ # when "0128"
127
+ # @data.resolution_unit = data
128
+ # when "0129"
129
+ # @data.page_number = data
130
+ # when "012C"
131
+ # @data.color_response_unit = data
132
+ # when "012D"
133
+ # @data.transfer_function = data
134
+ when "0131"
135
+ @data.software = interpret_as_string(data)
136
+ when "0132"
137
+ @data.modify_date = interpret_as_date_time(data)
138
+ # when "013B"
139
+ # @data.artist = data
140
+ # when "013C"
141
+ # @data.host_computer = data
142
+ # when "013D"
143
+ # @data.predictor = data
144
+ # when "013E"
145
+ # @data.white_point = data
146
+ # when "013F"
147
+ # @data.primary_chromaticities = data
148
+ # when "0140"
149
+ # @data.color_map = data
150
+ # when "0141"
151
+ # @data.halftone_hints = data
152
+ # when "0142"
153
+ # @data.tile_width = data
154
+ # when "0143"
155
+ # @data.tile_length = data
156
+ # when "0144"
157
+ # @data.tile_offsets = data
158
+ # when "0145"
159
+ # @data.tile_byte_counts = data
160
+ # when "0146"
161
+ # @data.bad_fax_lines = data
162
+ # when "0147"
163
+ # @data.clean_fax_data = data
164
+ # when "0148"
165
+ # @data.consecutive_bad_fax_lines = data
166
+ # when "014A"
167
+ # @data.sub_ifd = data
168
+ # when "014C"
169
+ # @data.ink_set = data
170
+ # when "014D"
171
+ # @data.ink_names = data
172
+ # when "014E"
173
+ # @data.number_of_inks = data
174
+ # when "0150"
175
+ # @data.dot_range = data
176
+ # when "0151"
177
+ # @data.target_printer = data
178
+ # when "0152"
179
+ # @data.extra_samples = data
180
+ # when "0153"
181
+ # @data.sample_format = data
182
+ # when "0154"
183
+ # @data.s_min_sample_value = data
184
+ # when "0155"
185
+ # @data.s_max_sample_value = data
186
+ # when "0156"
187
+ # @data.transfer_range = data
188
+ # when "0157"
189
+ # @data.clip_path = data
190
+ # when "0158"
191
+ # @data.x_clip_path_units = data
192
+ # when "0159"
193
+ # @data.y_clip_path_units = data
194
+ # when "015A"
195
+ # @data.indexed = data
196
+ # when "015B"
197
+ # @data.jpeg_tables = data
198
+ # when "015F"
199
+ # @data.opi_proxy = data
200
+ # when "0190"
201
+ # @data.global_parameters_ifd = data
202
+ # when "0191"
203
+ # @data.profile_type = data
204
+ # when "0192"
205
+ # @data.fax_profile = data
206
+ # when "0193"
207
+ # @data.coding_methods = data
208
+ # when "0194"
209
+ # @data.version_year = data
210
+ # when "0195"
211
+ # @data.mode_number = data
212
+ # when "01B1"
213
+ # @data.decode = data
214
+ # when "01B2"
215
+ # @data.default_image_color = data
216
+ # when "01B3"
217
+ # @data.t82_options = data
218
+ # when "01B5"
219
+ # @data.jpeg_tables = data
220
+ # when "0200"
221
+ # @data.jpeg_proc = data
222
+ # when "0201"
223
+ # @data.thumbnail_offset = data
224
+ # when "0202"
225
+ # @data.thumbnail_length = data
226
+ # when "0203"
227
+ # @data.jpeg_restart_interval = data
228
+ # when "0205"
229
+ # @data.jpeg_lossless_predictors = data
230
+ # when "0206"
231
+ # @data.jpeg_point_transforms = data
232
+ # when "0207"
233
+ # @data.jpeg_q_tables = data
234
+ # when "0208"
235
+ # @data.jpeg_dc_tables = data
236
+ # when "0209"
237
+ # @data.jpeg_ac_tables = data
238
+ # when "0211"
239
+ # @data.y_cb_cr_coefficients = data
240
+ # when "0212"
241
+ # @data.y_cb_cr_subsampling = data
242
+ # when "0213"
243
+ # @data.y_cb_cr_positioning = data
244
+ # when "0214"
245
+ # @data.reference_black_white = data
246
+ # when "022F"
247
+ # @data.strip_row_counts = data
248
+ # when "02BC"
249
+ # @data.application_notes = data
250
+ # when "03E7"
251
+ # @data.uspt_omiscellaneous = data
252
+ # when "1000"
253
+ # @data.related_image_file_format = data
254
+ # when "1001"
255
+ # @data.related_image_width = data
256
+ # when "1002"
257
+ # @data.related_image_height = data
258
+ # when "4746"
259
+ # @data.rating = data
260
+ # when "4747"
261
+ # @data.xp_dip_xml = data
262
+ # when "4748"
263
+ # @data.stitch_info = data
264
+ # when "4749"
265
+ # @data.rating_percent = data
266
+ # when "800D"
267
+ # @data.image_id = data
268
+ # when "80A3"
269
+ # @data.wang_tag_1 = data
270
+ # when "80A4"
271
+ # @data.wang_annotation = data
272
+ # when "80A5"
273
+ # @data.wang_tag_3 = data
274
+ # when "80A6"
275
+ # @data.wang_tag_4 = data
276
+ # when "80E3"
277
+ # @data.matteing = data
278
+ # when "80E4"
279
+ # @data.data_type = data
280
+ # when "80E5"
281
+ # @data.image_depth = data
282
+ # when "80E6"
283
+ # @data.tile_depth = data
284
+ # when "827D"
285
+ # @data.mode_l2 = data
286
+ # when "828D"
287
+ # @data.cfa_repeat_pattern_dim = data
288
+ # when "828E"
289
+ # @data.cfa_pattern_2 = data
290
+ # when "828F"
291
+ # @data.battery_level = data
292
+ # when "8290"
293
+ # @data.kodak_ifd = data
294
+ # when "8298"
295
+ # @data.copyright = data
296
+ # when "829A"
297
+ # @data.exposure_time = data
298
+ # when "829D"
299
+ # @data.f_number = data
300
+ # when "82A5"
301
+ # @data.md_file_tag = data
302
+ # when "82A6"
303
+ # @data.md_scale_pixel = data
304
+ # when "82A7"
305
+ # @data.md_color_table = data
306
+ # when "82A8"
307
+ # @data.md_lab_name = data
308
+ # when "82A9"
309
+ # @data.md_sample_info = data
310
+ # when "82AA"
311
+ # @data.md_prep_date = data
312
+ # when "82AB"
313
+ # @data.md_prep_time = data
314
+ # when "82AC"
315
+ # @data.mdfileunits = data
316
+ # when "830E"
317
+ # @data.pixel_scale = data
318
+ # when "8335"
319
+ # @data.advent_scale = data
320
+ # when "8336"
321
+ # @data.advent_revision = data
322
+ # when "835C"
323
+ # @data.uic_1_tag = data
324
+ # when "835D"
325
+ # @data.uic_2_tag = data
326
+ # when "835E"
327
+ # @data.uic_3_tag = data
328
+ # when "835F"
329
+ # @data.uic_4_tag = data
330
+ # when "83BB"
331
+ # @data.iptc_naa = data
332
+ # when "847E"
333
+ # @data.intergraph_packet_data = data
334
+ # when "847F"
335
+ # @data.intergraph_flag_registers = data
336
+ # when "8480"
337
+ # @data.intergraph_matrix = data
338
+ # when "8481"
339
+ # @data.ingr_reserved = data
340
+ # when "8482"
341
+ # @data.model_tie_point = data
342
+ # when "84E0"
343
+ # @data.site = data
344
+ # when "84E1"
345
+ # @data.color_sequence = data
346
+ # when "84E2"
347
+ # @data.it8_header = data
348
+ # when "84E3"
349
+ # @data.raster_padding = data
350
+ # when "84E4"
351
+ # @data.bits_per_run_length = data
352
+ # when "84E5"
353
+ # @data.bits_per_extended_run_length = data
354
+ # when "84E6"
355
+ # @data.color_table = data
356
+ # when "84E7"
357
+ # @data.image_color_indicator = data
358
+ # when "84E8"
359
+ # @data.background_color_indicator = data
360
+ # when "84E9"
361
+ # @data.image_color_value = data
362
+ # when "84EA"
363
+ # @data.background_color_value = data
364
+ # when "84EB"
365
+ # @data.pixel_intensity_range = data
366
+ # when "84EC"
367
+ # @data.transparency_indicator = data
368
+ # when "84ED"
369
+ # @data.color_characterization = data
370
+ # when "84EE"
371
+ # @data.hc_usage = data
372
+ # when "84EF"
373
+ # @data.trap_indicator = data
374
+ # when "84F0"
375
+ # @data.cmyk_equivalent = data
376
+ # when "8546"
377
+ # @data.sem_info = data
378
+ # when "8568"
379
+ # @data.afcp_iptc = data
380
+ # when "85B8"
381
+ # @data.pixel_magic_jbig_options = data
382
+ # when "85D8"
383
+ # @data.model_transform = data
384
+ # when "8602"
385
+ # @data.wb_grgb_levels = data
386
+ # when "8606"
387
+ # @data.leaf_data = data
388
+ # when "8649"
389
+ # @data.photoshop_settings = data
390
+ when "8769"
391
+ @data.exif_offset = interpret_as_number(data)
392
+ # when "8773"
393
+ # @data.icc_profile = data
394
+ # when "877F"
395
+ # @data.tiff_fxextensions = data
396
+ # when "8780"
397
+ # @data.multi_profiles = data
398
+ # when "8781"
399
+ # @data.shared_data = data
400
+ # when "8782"
401
+ # @data.t88_options = data
402
+ # when "87AC"
403
+ # @data.image_layer = data
404
+ # when "87AF"
405
+ # @data.geo_tiff_directory = data
406
+ # when "87B0"
407
+ # @data.geo_tiff_double_params = data
408
+ # when "87B1"
409
+ # @data.geo_tiff_ascii_params = data
410
+ # when "8822"
411
+ # @data.exposure_program = data
412
+ # when "8824"
413
+ # @data.spectral_sensitivity = data
414
+ # when "8825"
415
+ # @data.gps_info = data
416
+ # when "8827"
417
+ # @data.iso = data
418
+ # when "8828"
419
+ # @data.opto_electric_conv_factor = data
420
+ # when "8829"
421
+ # @data.interlace = data
422
+ # when "882A"
423
+ # @data.time_zone_offset = data
424
+ # when "882B"
425
+ # @data.self_timer_mode = data
426
+ # when "8830"
427
+ # @data.sensitivity_type = data
428
+ # when "8831"
429
+ # @data.standard_output_sensitivity = data
430
+ # when "8832"
431
+ # @data.recommended_exposure_index = data
432
+ # when "8833"
433
+ # @data.iso_speed = data
434
+ # when "8834"
435
+ # @data.iso_speed_latitude_yyy = data
436
+ # when "8835"
437
+ # @data.iso_speed_latitude_zzz = data
438
+ # when "885C"
439
+ # @data.fax_recv_params = data
440
+ # when "885D"
441
+ # @data.fax_sub_address = data
442
+ # when "885E"
443
+ # @data.fax_recv_time = data
444
+ # when "888A"
445
+ # @data.leaf_subifd = data
446
+ # when "9000"
447
+ # @data.exif_version = data
448
+ when "9003"
449
+ @data.date_time_original = interpret_as_date_time(data)
450
+ when "9004"
451
+ @data.create_date = interpret_as_date_time(data)
452
+ # when "9101"
453
+ # @data.components_configuration = data
454
+ # when "9102"
455
+ # @data.compressed_bits_per_pixel = data
456
+ # when "9201"
457
+ # @data.shutter_speed_value = data
458
+ # when "9202"
459
+ # @data.aperture_value = data
460
+ # when "9203"
461
+ # @data.brightness_value = data
462
+ # when "9204"
463
+ # @data.exposure_compensation = data
464
+ # when "9205"
465
+ # @data.max_aperture_value = data
466
+ # when "9206"
467
+ # @data.subject_distance = data
468
+ # when "9207"
469
+ # @data.metering_mode = data
470
+ # when "9208"
471
+ # @data.light_source = data
472
+ # when "9209"
473
+ # @data.flash = data
474
+ # when "920A"
475
+ # @data.focal_length = data
476
+ # when "920B"
477
+ # @data.flash_energy = data
478
+ # when "920C"
479
+ # @data.spatial_frequencyresponse = data
480
+ # when "920D"
481
+ # @data.noise = data
482
+ # when "920E"
483
+ # @data.focal_plane_x_resolution = data
484
+ # when "920F"
485
+ # @data.focal_plane_y_resolution = data
486
+ # when "9210"
487
+ # @data.focal_plane_resolution_unit = data
488
+ # when "9211"
489
+ # @data.image_number = data
490
+ # when "9212"
491
+ # @data.security_classification = data
492
+ # when "9213"
493
+ # @data.image_history = data
494
+ # when "9214"
495
+ # @data.subject_area = data
496
+ # when "9215"
497
+ # @data.exposure_index = data
498
+ # when "9216"
499
+ # @data.tiff_ep_standard_id = data
500
+ # when "9217"
501
+ # @data.sensing_method = data
502
+ # when "923A"
503
+ # @data.cip3_data_file = data
504
+ # when "923B"
505
+ # @data.cip3_sheet = data
506
+ # when "923C"
507
+ # @data.cip3_side = data
508
+ # when "923F"
509
+ # @data.sto_nits = data
510
+ # when "927C"
511
+ # @data.maker_note_vendor = data # VENDOR can be Apple, Nikon, Canon, etc
512
+ # when "9286"
513
+ # @data.user_comment = data
514
+ # when "9290"
515
+ # @data.sub_sec_time = data
516
+ # when "9291"
517
+ # @data.sub_sec_time_original = data
518
+ # when "9292"
519
+ # @data.sub_sec_time_digitized = data
520
+ # when "932F"
521
+ # @data.ms_document_text = data
522
+ # when "9330"
523
+ # @data.ms_property_set_storage = data
524
+ # when "9331"
525
+ # @data.ms_document_text_position = data
526
+ # when "935C"
527
+ # @data.image_source_data = data
528
+ # when "9C9B"
529
+ # @data.xp_title = data
530
+ # when "9C9C"
531
+ # @data.xp_comment = data
532
+ # when "9C9D"
533
+ # @data.xp_author = data
534
+ # when "9C9E"
535
+ # @data.xp_keywords = data
536
+ # when "9C9F"
537
+ # @data.xp_subject = data
538
+ # when "A000"
539
+ # @data.flashpix_version = data
540
+ # when "A001"
541
+ # @data.color_space = data
542
+ # when "A002"
543
+ # @data.exif_image_width = data
544
+ # when "A003"
545
+ # @data.exif_image_height = data
546
+ # when "A004"
547
+ # @data.related_sound_file = data
548
+ # when "A005"
549
+ # @data.interop_offset = data
550
+ # when "A20B"
551
+ # @data.flash_energy = data
552
+ # when "A20C"
553
+ # @data.spatial_frequency_response = data
554
+ # when "A20D"
555
+ # @data.noise = data
556
+ # when "A20E"
557
+ # @data.focal_plane_x_resolution = data
558
+ # when "A20F"
559
+ # @data.focal_plane_y_resolution = data
560
+ # when "A210"
561
+ # @data.focal_plane_resolution_unit = data
562
+ # when "A211"
563
+ # @data.image_number = data
564
+ # when "A212"
565
+ # @data.security_classification = data
566
+ # when "A213"
567
+ # @data.image_history = data
568
+ # when "A214"
569
+ # @data.subject_location = data
570
+ # when "A215"
571
+ # @data.exposure_index = data
572
+ # when "A216"
573
+ # @data.tiff_ep_standard_id = data
574
+ # when "A217"
575
+ # @data.sensing_method = data
576
+ # when "A300"
577
+ # @data.file_source = data
578
+ # when "A301"
579
+ # @data.scene_type = data
580
+ # when "A302"
581
+ # @data.cfa_pattern = data
582
+ # when "A401"
583
+ # @data.custom_rendered = data
584
+ # when "A402"
585
+ # @data.exposure_mode = data
586
+ # when "A403"
587
+ # @data.white_balance = data
588
+ # when "A404"
589
+ # @data.digital_zoom_ratio = data
590
+ # when "A405"
591
+ # @data.focal_length_in_35mm_format = data
592
+ # when "A406"
593
+ # @data.scene_capture_type = data
594
+ # when "A407"
595
+ # @data.gain_control = data
596
+ # when "A408"
597
+ # @data.contrast = data
598
+ # when "A409"
599
+ # @data.saturation = data
600
+ # when "A40A"
601
+ # @data.sharpness = data
602
+ # when "A40B"
603
+ # @data.device_setting_description = data
604
+ # when "A40C"
605
+ # @data.subject_distance_range = data
606
+ # when "A420"
607
+ # @data.image_unique_id = data
608
+ # when "A430"
609
+ # @data.owner_name = data
610
+ # when "A431"
611
+ # @data.serial_number = data
612
+ # when "A432"
613
+ # @data.lens_info = data
614
+ # when "A433"
615
+ # @data.lens_make = data
616
+ # when "A434"
617
+ # @data.lens_model = data
618
+ # when "A435"
619
+ # @data.lens_serial_number = data
620
+ # when "A480"
621
+ # @data.gdal_metadata = data
622
+ # when "A481"
623
+ # @data.gdal_no_data = data
624
+ # when "A500"
625
+ # @data.gamma = data
626
+ # when "AFC0"
627
+ # @data.expand_software = data
628
+ # when "AFC1"
629
+ # @data.expand_lens = data
630
+ # when "AFC2"
631
+ # @data.expand_film = data
632
+ # when "AFC3"
633
+ # @data.expand_filter_lens = data
634
+ # when "AFC4"
635
+ # @data.expand_scanner = data
636
+ # when "AFC5"
637
+ # @data.expand_flash_lamp = data
638
+ # when "BC01"
639
+ # @data.pixel_format = data
640
+ # when "BC02"
641
+ # @data.transformation = data
642
+ # when "BC03"
643
+ # @data.uncompressed = data
644
+ # when "BC04"
645
+ # @data.image_type = data
646
+ # when "BC80"
647
+ # @data.image_width = data
648
+ # when "BC81"
649
+ # @data.image_height = data
650
+ # when "BC82"
651
+ # @data.width_resolution = data
652
+ # when "BC83"
653
+ # @data.height_resolution = data
654
+ # when "BCC0"
655
+ # @data.image_offset = data
656
+ # when "BCC1"
657
+ # @data.image_byte_count = data
658
+ # when "BCC2"
659
+ # @data.alpha_offset = data
660
+ # when "BCC3"
661
+ # @data.alpha_byte_count = data
662
+ # when "BCC4"
663
+ # @data.image_data_discard = data
664
+ # when "BCC5"
665
+ # @data.alpha_data_discard = data
666
+ # when "C427"
667
+ # @data.oce_scanjob_desc = data
668
+ # when "C428"
669
+ # @data.oce_application_selector = data
670
+ # when "C429"
671
+ # @data.oce_id_number = data
672
+ # when "C42A"
673
+ # @data.oce_image_logic = data
674
+ # when "C44F"
675
+ # @data.annotations = data
676
+ # when "C4A5"
677
+ # @data.print_im = data
678
+ # when "C573"
679
+ # @data.original_filename = data
680
+ # when "C580"
681
+ # @data.uspto_original_content_type = data
682
+ # when "C612"
683
+ # @data.dng_version = data
684
+ # when "C613"
685
+ # @data.dng_backward_version = data
686
+ # when "C614"
687
+ # @data.unique_camera_model = data
688
+ # when "C615"
689
+ # @data.localized_camera_model = data
690
+ # when "C616"
691
+ # @data.cfa_plane_color = data
692
+ # when "C617"
693
+ # @data.cfa_layout = data
694
+ # when "C618"
695
+ # @data.linearization_table = data
696
+ # when "C619"
697
+ # @data.black_level_repeat_dim = data
698
+ # when "C61A"
699
+ # @data.black_level = data
700
+ # when "C61B"
701
+ # @data.black_level_delta_h = data
702
+ # when "C61C"
703
+ # @data.black_level_delta_v = data
704
+ # when "C61D"
705
+ # @data.white_level = data
706
+ # when "C61E"
707
+ # @data.default_scale = data
708
+ # when "C61F"
709
+ # @data.default_crop_origin = data
710
+ # when "C620"
711
+ # @data.default_crop_size = data
712
+ # when "C621"
713
+ # @data.color_matrix_1 = data
714
+ # when "C622"
715
+ # @data.color_matrix_2 = data
716
+ # when "C623"
717
+ # @data.camera_calibration_1 = data
718
+ # when "C624"
719
+ # @data.camera_calibration_2 = data
720
+ # when "C625"
721
+ # @data.reduction_matrix_1 = data
722
+ # when "C626"
723
+ # @data.reduction_matrix_2 = data
724
+ # when "C627"
725
+ # @data.analog_balance = data
726
+ # when "C628"
727
+ # @data.asshot_neutral = data
728
+ # when "C629"
729
+ # @data.asshot_white_xy = data
730
+ # when "C62A"
731
+ # @data.baseline_exposure = data
732
+ # when "C62B"
733
+ # @data.baseline_noise = data
734
+ # when "C62C"
735
+ # @data.baseline_sharpness = data
736
+ # when "C62D"
737
+ # @data.bayer_green_split = data
738
+ # when "C62E"
739
+ # @data.linear_response_limit = data
740
+ # when "C62F"
741
+ # @data.camera_serial_number = data
742
+ # when "C630"
743
+ # @data.dng_lens_info = data
744
+ # when "C631"
745
+ # @data.chroma_blur_radius = data
746
+ # when "C632"
747
+ # @data.anti_alias_strength = data
748
+ # when "C633"
749
+ # @data.shadow_scale = data
750
+ # when "C634"
751
+ # @data.sr2_private = data
752
+ # when "C635"
753
+ # @data.maker_note_safety = data
754
+ # when "C640"
755
+ # @data.raw_image_segmentation = data
756
+ # when "C65A"
757
+ # @data.calibration_illuminant_1 = data
758
+ # when "C65B"
759
+ # @data.calibration_illuminant_2 = data
760
+ # when "C65C"
761
+ # @data.best_quality_scale = data
762
+ # when "C65D"
763
+ # @data.raw_data_unique_id = data
764
+ # when "C660"
765
+ # @data.alias_layer_metadata = data
766
+ # when "C68B"
767
+ # @data.original_raw_file_name = data
768
+ # when "C68C"
769
+ # @data.original_raw_file_data = data
770
+ # when "C68D"
771
+ # @data.active_area = data
772
+ # when "C68E"
773
+ # @data.masked_areas = data
774
+ # when "C68F"
775
+ # @data.asshot_icc_profile = data
776
+ # when "C690"
777
+ # @data.asshot_pre_profile_matrix = data
778
+ # when "C691"
779
+ # @data.current_icc_profile = data
780
+ # when "C692"
781
+ # @data.current_pre_profile_matrix = data
782
+ # when "C6BF"
783
+ # @data.colorimetric_reference = data
784
+ # when "C6D2"
785
+ # @data.panasonic_title = data
786
+ # when "C6D3"
787
+ # @data.panasonic_title_2 = data
788
+ # when "C6F3"
789
+ # @data.camera_calibration_sig = data
790
+ # when "C6F4"
791
+ # @data.profile_calibration_sig = data
792
+ # when "C6F5"
793
+ # @data.profile_ifd = data
794
+ # when "C6F6"
795
+ # @data.asshot_profile_name = data
796
+ # when "C6F7"
797
+ # @data.noise_reduction_applied = data
798
+ # when "C6F8"
799
+ # @data.profile_name = data
800
+ # when "C6F9"
801
+ # @data.profile_hue_sat_mapdims = data
802
+ # when "C6FA"
803
+ # @data.profile_hue_sat_map_data_1 = data
804
+ # when "C6FB"
805
+ # @data.profile_hue_sat_map_data_2 = data
806
+ # when "C6FC"
807
+ # @data.profile_tone_curve = data
808
+ # when "C6FD"
809
+ # @data.profile_embed_policy = data
810
+ # when "C6FE"
811
+ # @data.profile_copyright = data
812
+ # when "C714"
813
+ # @data.forward_matrix_1 = data
814
+ # when "C715"
815
+ # @data.forward_matrix_2 = data
816
+ # when "C716"
817
+ # @data.preview_application_name = data
818
+ # when "C717"
819
+ # @data.preview_application_version = data
820
+ # when "C718"
821
+ # @data.preview_settings_name = data
822
+ # when "C719"
823
+ # @data.preview_settings_digest = data
824
+ # when "C71A"
825
+ # @data.preview_color_space = data
826
+ # when "C71B"
827
+ # @data.preview_date_time = data
828
+ # when "C71C"
829
+ # @data.raw_image_digest = data
830
+ # when "C71D"
831
+ # @data.original_raw_file_digest = data
832
+ # when "C71E"
833
+ # @data.sub_tile_block_size = data
834
+ # when "C71F"
835
+ # @data.row_interleave_factor = data
836
+ # when "C725"
837
+ # @data.profile_look_table_dims = data
838
+ # when "C726"
839
+ # @data.profile_look_table_data = data
840
+ # when "C740"
841
+ # @data.opcode_list_1 = data
842
+ # when "C741"
843
+ # @data.opcode_list_2 = data
844
+ # when "C74E"
845
+ # @data.opcode_list_3 = data
846
+ # when "C761"
847
+ # @data.noise_profile = data
848
+ # when "C763"
849
+ # @data.time_codes = data
850
+ # when "C764"
851
+ # @data.frame_rate = data
852
+ # when "C772"
853
+ # @data.t_stop = data
854
+ # when "C789"
855
+ # @data.reel_name = data
856
+ # when "C791"
857
+ # @data.original_default_final_size = data
858
+ # when "C792"
859
+ # @data.original_best_quality_size = data
860
+ # when "C793"
861
+ # @data.original_default_crop_size = data
862
+ # when "C7A1"
863
+ # @data.camera_label = data
864
+ # when "C7A3"
865
+ # @data.profile_hue_sat_map_encoding = data
866
+ # when "C7A4"
867
+ # @data.profile_look_table_encoding = data
868
+ # when "C7A5"
869
+ # @data.baseline_exposure_offset = data
870
+ # when "C7A6"
871
+ # @data.default_black_render = data
872
+ # when "C7A7"
873
+ # @data.new_raw_image_digest = data
874
+ # when "C7A8"
875
+ # @data.raw_to_preview_gain = data
876
+ # when "C7B5"
877
+ # @data.default_user_crop = data
878
+ # when "EA1C"
879
+ # @data.padding = data
880
+ # when "EA1D"
881
+ # @data.offset_schema = data
882
+ # when "FDE8"
883
+ # @data.owner_name = data
884
+ # when "FDE9"
885
+ # @data.serial_number = data
886
+ # when "FDEA"
887
+ # @data.lens = data
888
+ # when "FE00"
889
+ # @data.kdc_ifd = data
890
+ # when "FE4C"
891
+ # @data.raw_file = data
892
+ # when "FE4D"
893
+ # @data.converter = data
894
+ # when "FE4E"
895
+ # @data.white_balance = data
896
+ # when "FE51"
897
+ # @data.exposure = data
898
+ # when "FE52"
899
+ # @data.shadows = data
900
+ # when "FE53"
901
+ # @data.brightness = data
902
+ # when "FE54"
903
+ # @data.contrast = data
904
+ # when "FE55"
905
+ # @data.saturation = data
906
+ # when "FE56"
907
+ # @data.sharpness = data
908
+ # when "FE57"
909
+ # @data.smoothness = data
910
+ # when "FE58"
911
+ # @data.moire_filter = data
912
+ end
913
+
914
+ end
915
+
916
+ end
917
+
918
+ end
@@ -0,0 +1,176 @@
1
+ module ExifUtility
2
+ class Parser
3
+
4
+ def initialize(filename, data)
5
+ @data = data
6
+ @interpreter = Interpreter.new(@data)
7
+
8
+ file = File.open(filename, 'rb')
9
+
10
+ # Check to make sure it's an image.
11
+ # Every JPEG starts with 0xFFD8 the marker for a JPEG SOI (Start of image)
12
+ unless file.read(2).unpack('H*')[0].upcase == "FFD8"
13
+ raise "#{filename} is not a JPEG"
14
+ end
15
+
16
+ # Exif data starts with the AAP1 Marker: 0xFFE1
17
+ # Sometimes there are other markers (0xFFE0) before, read past them until you get to 0xFFE1
18
+ first = file.read(1).unpack('H*')[0].upcase
19
+ while true
20
+ second = file.read(1).unpack('H*')[0].upcase
21
+
22
+ if first == "FF" and second == "E1"
23
+ break
24
+ end
25
+ first = second
26
+ end
27
+
28
+ # unless file.read(2).unpack('H*')[0].upcase == "FFE1"
29
+ # raise "#{filename} does not have Exif data"
30
+ # end
31
+
32
+ # The size of the APP1 data area (Exif data area)
33
+ exif_data_size = file.read(2).unpack('H*')[0].upcase.to_i(16) - 2 # note that this size includes the size of descriptor (2 bytes)
34
+
35
+ # Read in the Exif Header
36
+ # The Exif Header always starts with 0x457869660000
37
+ unless file.read(6).unpack('H*')[0].upcase == "457869660000"
38
+ raise "#{filename} does not have an Exif header"
39
+ end
40
+
41
+ exif_data_size -= 6 # subtract the size of the Exif Header (6 bytes)
42
+
43
+ # Read in all of APP1 Data
44
+ # @exif_data now starts at offset 0x0000
45
+ @exif_data = file.read(exif_data_size).unpack('H*')[0].upcase
46
+ @offset = 0
47
+ @alignment = :motorola
48
+
49
+
50
+ # Read in the TIFF Header (8 bytes)
51
+ # The first 2 bytes define the byte align of the TIFF data
52
+ # 0x4949 == "II". This is the Intel alignment (little-endian).
53
+ # 0x4d4d == "MM". This is the Motorola alignment (big-endian).
54
+ # Note that this alignment is only for the Exif data, not the JPEG data
55
+ # How the alignments differ:
56
+ # 0x12345678 is stored as 0x12 0x34 0x56 0x78 using Motorola align
57
+ # 0x12345678 is stored as 0x78 0x56 0x34 0x12 using Intel align
58
+ # But you can't just
59
+ case read(2)
60
+ when "4949"
61
+ @alignment = :intel
62
+ when "4D4D"
63
+ @alignment = :motorola
64
+ else
65
+ raise 'The TIFF Header does not have a byte alignment'
66
+ end
67
+
68
+ # The next 2 bytes are always 0x002A
69
+ unless read(2) == "002A"
70
+ raise 'Parsing error: not 0x002A'
71
+ end
72
+
73
+ # The last 4 bytes of the TIFF header are an offset to the first IFD (Image File Directory).
74
+ # Offset is counted from the beginning of the TIFF header and is usually 0x00000008.
75
+ # The Tiff header, including these 4 bytes, is 8 bytes total, so 0x00000008 is immediately after to the TIFF header
76
+ # Get us to the beginning of IFD0
77
+ @offset = read(4).to_i(16)
78
+
79
+ # The first IFD, IFD0, is the IFD of the main image.
80
+ # IFD1 is the IFD of the thumbnail image.
81
+ # IFDs contain image information data but IFD0 and IFD1 don't contain any digicam info such as shutter speed, focal length, etc.
82
+ # IFD0 always contains a special Tag Exif Offset (0x8769) which shows an offset to Exif SufIFD which is IFD formatted data that contains digicam info.
83
+
84
+ # Parse the first IFD
85
+ parse_IFD
86
+
87
+ # After an IFD there are 4 bytes representing the offset to the next IFD.
88
+ # Note that the next IFD is different from Exif SubIFD
89
+ # If they are 0x00000000 then there are no more IFDs
90
+ offset_to_next_IFD = read(4).to_i(16)
91
+
92
+ # Then jump to the offset for Exif SubIFD
93
+ set_offset(@data.exif_offset)
94
+ # Then parse the Exif SubIFD which contains digicam information
95
+ parse_IFD
96
+
97
+
98
+ ## TODO: Parse the IFD linked to by IFD0
99
+
100
+ end
101
+
102
+
103
+
104
+ def parse_IFD
105
+ # The first 2 bytes are the number of directory entries contained in this IFD
106
+ @entries = read(2).to_i(16)
107
+
108
+ # Next are the directory entires, 12 bytes per entry, from entry (0) to (@entries - 1)
109
+ @entries.times do
110
+ parse_entry
111
+ end
112
+
113
+
114
+ end
115
+
116
+ def parse_entry
117
+ tag = read(2)
118
+ data_format = read(2)
119
+ number_of_components = read(4).to_i(16)
120
+ data_value = read(4)
121
+
122
+ bytes_of_data = @interpreter.size_from_data_format(data_format) * number_of_components
123
+ if bytes_of_data > 4
124
+ # interpret the datavalue as an offset
125
+ result = @interpreter.interpret(tag, read_at_offset(bytes_of_data, data_value.to_i(16))) # in this scenario data_value is the offset to the data
126
+ else
127
+ # interpret the datavalue as the data
128
+ result = @interpreter.interpret(tag, data_value)
129
+ end
130
+
131
+ # Data is saved in the interpreter
132
+ # @data.add_data(tag, result)
133
+
134
+ end
135
+
136
+
137
+
138
+
139
+ # Section for methods that work with reading in bytes
140
+ # Reads in data from @exif_data
141
+
142
+ def set_offset(new_offset)
143
+ @offset = new_offset
144
+ end
145
+
146
+ # have to chck if the data should be read as little or big endian
147
+ def read(num_bytes)
148
+ # convert to offsets in the array
149
+ start_offset = (@offset * 2)
150
+ end_offset = start_offset + (num_bytes * 2) - 1
151
+
152
+ # Check the alignment and convert if it's Intel align
153
+ if @alignment == :intel
154
+ # Convert little to big endian with an atomic element size of 1 byte
155
+ bytes = @exif_data[start_offset..end_offset].scan(/(..)/).reverse.join
156
+ else
157
+ # Assume by default that it's Motorola type align
158
+ bytes = @exif_data[start_offset..end_offset]
159
+ end
160
+
161
+ @offset += num_bytes
162
+ bytes
163
+ end
164
+
165
+ # always read big endian
166
+ def read_at_offset(num_bytes, offset)
167
+ # convert to offsets in the array
168
+ start_offset = (offset * 2)
169
+ end_offset = start_offset + (num_bytes * 2) - 1
170
+
171
+ @exif_data[start_offset..end_offset]
172
+ end
173
+
174
+ end
175
+
176
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exif_utility
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jason Wang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-14 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Exif Utility is a module for reading Exif information from JPEGs
14
+ email: jasonbwangjbw@gmail.com
15
+ executables:
16
+ - exif_utility
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/exif_utility
21
+ - lib/exif_utility.rb
22
+ - lib/exif_utility/data.rb
23
+ - lib/exif_utility/interpreter.rb
24
+ - lib/exif_utility/parser.rb
25
+ homepage: https://github.com/jasonbw/exif_utility
26
+ licenses:
27
+ - MIT
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.2.1
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: A module for reading Exif information from JPEGs
49
+ test_files: []