exifr 0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+