exifr 0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README +12 -0
  2. data/lib/exif.rb +146 -0
  3. data/lib/exifr.rb +5 -0
  4. data/lib/jpeg.rb +69 -0
  5. data/lib/tiff_header.rb +75 -0
  6. metadata +49 -0
data/README ADDED
@@ -0,0 +1,12 @@
1
+ = EXIF Reader
2
+ EXIF Reader is a module to read EXIF from JPEG images.
3
+
4
+ == Examples
5
+ EXIFR::JPEG.new('IMG_3422.JPG').width # -> 2272
6
+ EXIFR::JPEG.new('IMG_3422.JPG').exif.model # -> "Canon PowerShot G3"
7
+
8
+ == Author
9
+ R.W. van 't Veer
10
+
11
+ == Copyright
12
+ Copyright (c) 2006 R.W. van 't Veer
data/lib/exif.rb ADDED
@@ -0,0 +1,146 @@
1
+ # Copyright (c) 2006 - R.W. van 't Veer
2
+
3
+ module EXIFR
4
+ # = EXIF decoder
5
+ class EXIF < Hash
6
+ @@TAGS = {
7
+ 0x0100 => :image_width,
8
+ 0x0101 => :image_length,
9
+ 0x0102 => :bits_per_sample,
10
+ 0x0103 => :compression,
11
+ 0x0106 => :photometric_interpretation,
12
+ 0x010a => :fill_order,
13
+ 0x010d => :document_name,
14
+ 0x010e => :image_description,
15
+ 0x010f => :make,
16
+ 0x0110 => :model,
17
+ 0x0111 => :strip_offsets,
18
+ 0x0112 => :orientation,
19
+ 0x0115 => :samples_per_pixel,
20
+ 0x0116 => :rows_per_strip,
21
+ 0x0117 => :strip_byte_counts,
22
+ 0x011a => :xresolution,
23
+ 0x011b => :yresolution,
24
+ 0x011c => :planar_configuration,
25
+ 0x0128 => :resolution_unit,
26
+ 0x012d => :transfer_function,
27
+ 0x0131 => :software,
28
+ 0x0132 => :date_time,
29
+ 0x013b => :artist,
30
+ 0x013e => :white_point,
31
+ 0x013f => :primary_chromaticities,
32
+ 0x0156 => :transfer_range,
33
+ 0x0200 => :jpegproc,
34
+ 0x0201 => :jpeginterchange_format,
35
+ 0x0202 => :jpeginterchange_format_length,
36
+ 0x0211 => :ycb_cr_coefficients,
37
+ 0x0212 => :ycb_cr_sub_sampling,
38
+ 0x0213 => :ycb_cr_positioning,
39
+ 0x0214 => :reference_black_white,
40
+ 0x828d => :cfarepeat_pattern_dim,
41
+ 0x828e => :cfapattern,
42
+ 0x828f => :battery_level,
43
+ 0x8298 => :copyright,
44
+ 0x829a => :exposure_time,
45
+ 0x829d => :fnumber,
46
+ 0x83bb => :iptc_naa,
47
+ 0x8769 => :exif_offset,
48
+ 0x8773 => :inter_color_profile,
49
+ 0x8822 => :exposure_program,
50
+ 0x8824 => :spectral_sensitivity,
51
+ 0x8825 => :gpsinfo,
52
+ 0x8827 => :isospeed_ratings,
53
+ 0x8828 => :oecf,
54
+ 0x9000 => :exif_version,
55
+ 0x9003 => :date_time_original,
56
+ 0x9004 => :date_time_digitized,
57
+ 0x9101 => :components_configuration,
58
+ 0x9102 => :compressed_bits_per_pixel,
59
+ 0x9201 => :shutter_speed_value,
60
+ 0x9202 => :aperture_value,
61
+ 0x9203 => :brightness_value,
62
+ 0x9204 => :exposure_bias_value,
63
+ 0x9205 => :max_aperture_value,
64
+ 0x9206 => :subject_distance,
65
+ 0x9207 => :metering_mode,
66
+ 0x9208 => :light_source,
67
+ 0x9209 => :flash,
68
+ 0x920a => :focal_length,
69
+ 0x9214 => :subject_area,
70
+ 0x927c => :maker_note,
71
+ 0x9286 => :user_comment,
72
+ 0x9290 => :subsec_time,
73
+ 0x9291 => :subsec_time_orginal,
74
+ 0x9292 => :subsec_time_digitized,
75
+ 0xa000 => :flash_pix_version,
76
+ 0xa001 => :color_space,
77
+ 0xa002 => :pixel_xdimension,
78
+ 0xa003 => :pixel_ydimension,
79
+ 0xa004 => :related_sound_file,
80
+ 0xa005 => :interoperability_offset,
81
+ 0xa20b => :flash_energy,
82
+ 0xa20c => :spatial_frequency_response,
83
+ 0xa20e => :focal_plane_xresolution,
84
+ 0xa20f => :focal_plane_yresolution,
85
+ 0xa210 => :focal_plane_resolution_unit,
86
+ 0xa214 => :subject_location,
87
+ 0xa215 => :exposure_index,
88
+ 0xa217 => :sensing_method,
89
+ 0xa300 => :file_source,
90
+ 0xa301 => :scene_type,
91
+ 0xa302 => :cfapattern,
92
+ 0xa401 => :custom_rendered,
93
+ 0xa402 => :exposure_mode,
94
+ 0xa403 => :white_balance,
95
+ 0xa404 => :digital_zoom_ratio,
96
+ 0xa405 => :focal_len_in_35mm_film,
97
+ 0xa406 => :scene_capture_type,
98
+ 0xa407 => :gain_control,
99
+ 0xa408 => :contrast,
100
+ 0xa409 => :saturation,
101
+ 0xa40a => :sharpness,
102
+ 0xa40b => :device_setting_descr,
103
+ 0xa40c => :subject_dist_range,
104
+ 0xa420 => :image_unique_id
105
+ }
106
+ @@EXIF_HEADERS = [0x8769, 0x8825, 0xa005]
107
+ @@TIME_TAGS = [:date_time, :date_time_original, :date_time_digitized]
108
+
109
+ # +data+ the content of the JPEG APP1 frame without the EXIF marker
110
+ def initialize(data)
111
+ @data = data
112
+ traverse(TiffHeader.new(@data))
113
+ freeze
114
+ end
115
+
116
+ # convience
117
+ def method_missing(method, *args)
118
+ self[method]
119
+ end
120
+
121
+ private
122
+ def traverse(tiff)
123
+ tiff.fields.each do |f|
124
+ tag, value = @@TAGS[f.tag], f.value
125
+ value = (value.kind_of?(Array) && value.size == 1) ? value.first : value
126
+ if @@EXIF_HEADERS.include?(f.tag)
127
+ traverse(TiffHeader.new(@data, f.offset))
128
+ elsif tag
129
+ if @@TIME_TAGS.include? tag
130
+ self[tag] = value2time(value)
131
+ else
132
+ self[tag] = value
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ def value2time(value)
139
+ if value.sub(/\000.*$/, '') =~ /^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/
140
+ Time.mktime($1, $2, $3, $4, $5, $6)
141
+ else
142
+ value
143
+ end
144
+ end
145
+ end
146
+ end
data/lib/exifr.rb ADDED
@@ -0,0 +1,5 @@
1
+ # Copyright (c) 2006 - R.W. van 't Veer
2
+
3
+ require 'jpeg'
4
+ require 'exif'
5
+ require 'tiff_header'
data/lib/jpeg.rb ADDED
@@ -0,0 +1,69 @@
1
+ # Copyright (c) 2006 - R.W. van 't Veer
2
+
3
+ require 'rational'
4
+
5
+ module EXIFR
6
+ # = JPEG decoder
7
+ #
8
+ # == Examples
9
+ # EXIFR::JPEG.new('IMG_3422.JPG').width # -> 2272
10
+ # EXIFR::JPEG.new('IMG_3422.JPG').exif.model # -> "Canon PowerShot G3"
11
+ class JPEG
12
+ # image height
13
+ attr_reader :height
14
+ # image width
15
+ attr_reader :width
16
+ # number of bits per ???
17
+ attr_reader :bits
18
+ # image comment
19
+ attr_reader :comment
20
+ # hash of exif data if available
21
+ attr_reader :exif
22
+
23
+ # +file+ is a filename or an IO object
24
+ def initialize(file)
25
+ if file.kind_of? IO
26
+ examine(file)
27
+ else
28
+ File.open(file, 'rb') { |io| examine(io) }
29
+ end
30
+ end
31
+
32
+ # returns +true+ when EXIF data is available
33
+ def exif?
34
+ !exif.nil?
35
+ end
36
+
37
+ private
38
+ def examine(io)
39
+ raise 'malformed JPEG' unless io.getc == 0xFF && io.getc == 0xD8 # SOI
40
+
41
+ class << io
42
+ def readint; (readchar << 8) + readchar; end
43
+ def readframe; read(readint - 2); end
44
+ def readsof; [readint, readchar, readint, readint, readchar]; end
45
+ def next
46
+ c = readchar while c != 0xFF
47
+ c = readchar while c == 0xFF
48
+ c
49
+ end
50
+ end
51
+
52
+ while marker = io.next
53
+ case marker
54
+ when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF # SOF markers
55
+ length, @bits, @height, @width, components = io.readsof
56
+ raise 'malformed JPEG' unless length == 8 + components * 3
57
+ when 0xD9, 0xDA: break # EOI, SOS
58
+ when 0xFE: @comment = io.readframe # COM
59
+ when 0xE1: app1 = io.readframe # APP1, contains EXIF tag
60
+ else io.readframe # ignore frame
61
+ end
62
+ end
63
+
64
+ if app1 && EXIF
65
+ @exif = EXIF.new(app1[6..-1]) # rescue nil
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,75 @@
1
+ # Copyright (c) 2006 - R.W. van 't Veer
2
+
3
+ module EXIFR
4
+ class TiffHeader # :nodoc:
5
+ attr_reader :data, :fields
6
+
7
+ def initialize(data, offset = nil)
8
+ @data = data
9
+ @fields = []
10
+
11
+ class << @data
12
+ attr_accessor :short, :long
13
+ def readshort(pos); self[pos..(pos + 1)].unpack(@short)[0]; end
14
+ def readlong(pos); self[pos..(pos + 3)].unpack(@long)[0]; end
15
+ end
16
+
17
+ case @data[0..1]
18
+ when 'II'; @data.short, @data.long = 'v', 'V'
19
+ when 'MM'; @data.short, @data.long = 'n', 'N'
20
+ else; raise 'no II or MM marker found'
21
+ end
22
+
23
+ readIfds(offset || @data.readlong(4))
24
+ end
25
+
26
+ def readIfds(pos)
27
+ while pos != 0 do
28
+ num = @data.readshort(pos)
29
+ pos += 2
30
+
31
+ num.times do
32
+ fields << TiffField.new(@data, pos)
33
+ pos += 12
34
+ end
35
+
36
+ pos = @data.readlong(pos)
37
+ end
38
+ end
39
+ end
40
+
41
+ class TiffField # :nodoc:
42
+ attr_reader :tag, :offset, :value
43
+
44
+ def initialize(data, pos)
45
+ @tag, count, @offset = data.readshort(pos), data.readlong(pos + 4), data.readlong(pos + 8)
46
+
47
+ case data.readshort(pos + 2)
48
+ when 1, 6 # byte, signed byte
49
+ # TODO handle signed bytes
50
+ len, pack = count, proc { |d| d }
51
+ when 2 # ascii
52
+ len, pack = count, proc { |d| d.sub(/\000.*$/, '').strip }
53
+ when 3, 8 # short, signed short
54
+ # TODO handle signed
55
+ len, pack = count * 2, proc { |d| d.unpack(data.short + '*') }
56
+ when 4, 9 # long, signed long
57
+ # TODO handle signed
58
+ len, pack = count * 4, proc { |d| d.unpack(data.long + '*') }
59
+ when 5, 10
60
+ len, pack = count * 8, proc do |d|
61
+ r = []
62
+ d.unpack(data.long + '*').each_with_index do |v,i|
63
+ i % 2 == 0 ? r << [v] : r.last << v
64
+ end
65
+ r.map{|f| Rational.reduce(*f)}
66
+ end
67
+ end
68
+
69
+ if len && pack
70
+ start = len > 4 ? @offset : (pos + 8)
71
+ @value = pack[data[start..(start + len)]]
72
+ end
73
+ end
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: exifr
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.9"
7
+ date: 2006-04-14 00:00:00 +02:00
8
+ summary: EXIF Reader is a module to read EXIF from JPEG images.
9
+ require_paths:
10
+ - lib
11
+ email: remco@remvee.net
12
+ homepage: http://exifr.rubyforge.org/
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: exifr
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - R.W. van 't Veer
30
+ files:
31
+ - lib/exif.rb
32
+ - lib/exifr.rb
33
+ - lib/jpeg.rb
34
+ - lib/tiff_header.rb
35
+ - README
36
+ test_files: []
37
+
38
+ rdoc_options: []
39
+
40
+ extra_rdoc_files:
41
+ - README
42
+ executables: []
43
+
44
+ extensions: []
45
+
46
+ requirements: []
47
+
48
+ dependencies: []
49
+