exifparser 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +1 -0
- data/exifparser.gemspec +23 -0
- data/lib/exifparser.rb +265 -0
- data/lib/exifparser/makernote/canon.rb +502 -0
- data/lib/exifparser/makernote/fujifilm.rb +415 -0
- data/lib/exifparser/makernote/minolta.rb +84 -0
- data/lib/exifparser/makernote/mk_nikonflensname.rb +39 -0
- data/lib/exifparser/makernote/nikon.rb +267 -0
- data/lib/exifparser/makernote/nikon2.rb +581 -0
- data/lib/exifparser/makernote/nikonflensname.rb +438 -0
- data/lib/exifparser/makernote/olympus.rb +225 -0
- data/lib/exifparser/makernote/prove.rb +84 -0
- data/lib/exifparser/makernote/sigma.rb +237 -0
- data/lib/exifparser/pre-setup.rb +1 -0
- data/lib/exifparser/scan.rb +278 -0
- data/lib/exifparser/tag.rb +2298 -0
- data/lib/exifparser/thumbnail.rb +76 -0
- data/lib/exifparser/utils.rb +88 -0
- data/lib/exifparser/version.rb +3 -0
- data/sample/exifview.rb +279 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7616a451f2f84dd5f7e220b0199f67ca9a193fd3
|
4
|
+
data.tar.gz: 9ee05e177cec4badc1c71b6bad7f0e4708688f10
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d07432c7dde901cfc613824b40ce74c669d0f378f9c9dbf26848dc0264085d9e84c615d56f6c4279511bf73b6a1635b959711288ba2055f57338ca2e8d891366
|
7
|
+
data.tar.gz: d2c2e23601eb550b2d0adfffc9c35c5da88676d023a717ab3667a0bc3777f604125b02ddfb880b365bd195d969c063272d765d20392772127a0bb6ecdb1eccab
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 kp
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Exifparser
|
2
|
+
|
3
|
+
Exif tag parser written in pure Ruby
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'exifparser'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install exifparser
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
see also lib/exifparser.rb
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
30
|
+
|
31
|
+
## Original Author
|
32
|
+
Ryuichi Tamura <r-tam@fsinet.or.jp>
|
33
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/exifparser.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'exifparser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "exifparser"
|
8
|
+
spec.version = ExifParser::VERSION
|
9
|
+
spec.authors = ["kp"]
|
10
|
+
spec.email = ["knomura.1394@gmail.com"]
|
11
|
+
spec.description = %q{Exif tag parser written in pure Ruby}
|
12
|
+
spec.summary = %q{Exif tag parser written in pure Ruby}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "Ruby"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/lib/exifparser.rb
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
#= exifparser.rb - Exif tag parser written in pure ruby
|
4
|
+
#
|
5
|
+
#Author:: Ryuchi Tamura (r-tam@fsinet.or.jp)
|
6
|
+
#Copyright:: Copyright (C) 2002 Ryuichi Tamura.
|
7
|
+
#
|
8
|
+
# $Id: exifparser.rb,v 1.1.1.1 2002/12/16 07:59:00 tam Exp $
|
9
|
+
#
|
10
|
+
#== INTRODUCTION
|
11
|
+
#
|
12
|
+
#There are 2 classes you work with. ExifParser class is
|
13
|
+
#the Exif tag parser that parses all tags defined EXIF-2.2 standard,
|
14
|
+
#and many of extension tags uniquely defined by some digital equipment
|
15
|
+
#manufacturers. Currently, part of tags defined by FujiFilm, and
|
16
|
+
#Olympus is supported. After initialized with the path to image file
|
17
|
+
#of EXIF format, ExifParser will provides which tags are available in
|
18
|
+
#the image, and how you work with them.
|
19
|
+
#
|
20
|
+
#Tags availble from ExifParser is objects defined under Exif::Tag module,
|
21
|
+
#with its name being the class name. For example if you get "Make" tag
|
22
|
+
#from ExifParser, it is Exif::Tag::DateTime object. Inspecting it looks
|
23
|
+
#following:
|
24
|
+
#
|
25
|
+
# #<Exif::Tag::TIFF::Make ID=0x010f, IFD="IFD0" Name="Make", Format="Ascii" Value="FUJIFILM">
|
26
|
+
#
|
27
|
+
#here, ID is Tag ID defined in EXIF-2.2 standard, IFD is the name of
|
28
|
+
#Image File Directory, Name is String representation of tag ID, Format is
|
29
|
+
#string that shows how the data is formatted, and Value is the value of
|
30
|
+
#the tag. This is retrieved by Exif::Tag::Make#value.
|
31
|
+
#
|
32
|
+
#Another example. If you want to know whether flash was fired when the image
|
33
|
+
#was generated, ExifParser returns Exif::Tag::Flash object:
|
34
|
+
#
|
35
|
+
# tag = exif['Flash']
|
36
|
+
# p tag
|
37
|
+
# => #<Exif::Tag::Exif::Flash ID=0x9209, IFD="Exif" Name="Flash", Format="Unsigned short" Value="1">
|
38
|
+
# p tag.value
|
39
|
+
# => 1
|
40
|
+
#
|
41
|
+
#It may happen that diffrent IFDs have the same tag name. In this case,
|
42
|
+
#use Exif#tag(tagname, IFD)
|
43
|
+
#
|
44
|
+
#The value of the tag above, 1, is not clearly understood
|
45
|
+
#(supposed to be 'true', though). Exif::Tag::Flash#to_s will provides
|
46
|
+
#more human-readable form as String.
|
47
|
+
#
|
48
|
+
# tag.to_s #=> "Flash fired."
|
49
|
+
#
|
50
|
+
#many of these sentences are cited from Exif-2.2 standard.
|
51
|
+
#
|
52
|
+
#== USAGE
|
53
|
+
# require 'exifparser'
|
54
|
+
#
|
55
|
+
# exif = ExifParser.new("fujifilm.jpg")
|
56
|
+
#
|
57
|
+
# 1. get a tag value by its name('Make') or its ID (0x010f)
|
58
|
+
# exif['Make'] #=> 'FUJIFILM'
|
59
|
+
# exif[0x010f] #=> 'FUJIFILM'
|
60
|
+
#
|
61
|
+
# if the specified tag is not found, nil is returned.
|
62
|
+
#
|
63
|
+
# 2. to see the image has the value of specified tag
|
64
|
+
# exif.tag?('DateTime') #=> true
|
65
|
+
# exif.tag?('CameraID') #=> false
|
66
|
+
#
|
67
|
+
# 3. get all the tags contained in the image.
|
68
|
+
#
|
69
|
+
# exif.tags
|
70
|
+
#
|
71
|
+
# or, if you want to know all the tags defined in specific IFD,
|
72
|
+
#
|
73
|
+
# exif.tags(:IFD0) # get all the tags defined in IFD0
|
74
|
+
#
|
75
|
+
# you can traverse each tag and work on it.
|
76
|
+
#
|
77
|
+
# exif.each do |tag|
|
78
|
+
# p tag.to_s
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# # each tag in IFD0
|
82
|
+
# exif.each(:IFD0) do |ifd0_tag|
|
83
|
+
# p ifd0_tag.to_s
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# 4. extract thumbnail
|
87
|
+
#
|
88
|
+
# File.open("thumb.jpg") do |dest|
|
89
|
+
# exif.thumbnail dest
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# dest object must respond to '<<'.
|
93
|
+
#
|
94
|
+
require 'exifparser/scan'
|
95
|
+
|
96
|
+
module Exif
|
97
|
+
|
98
|
+
class Parser
|
99
|
+
#
|
100
|
+
# create a new object. fpath is String.
|
101
|
+
#
|
102
|
+
def initialize(fpath)
|
103
|
+
@fpath = fpath
|
104
|
+
@scanner = nil
|
105
|
+
File.open(fpath, "rb") do |f|
|
106
|
+
@scanner = Exif::Scanner.new(f)
|
107
|
+
@scanner.scan
|
108
|
+
end
|
109
|
+
@IFD0 = @scanner.result[:IFD0]
|
110
|
+
@IFD1 = @scanner.result[:IFD1]
|
111
|
+
@Exif = @scanner.result[:Exif]
|
112
|
+
@GPS = @scanner.result[:GPS]
|
113
|
+
@Interoperability = @scanner.result[:Interoperability]
|
114
|
+
@MakerNote = @scanner.result[:MakerNote]
|
115
|
+
@thumbnail = @scanner.result[:Thumbnail]
|
116
|
+
end
|
117
|
+
|
118
|
+
def inspect
|
119
|
+
sprintf("#<%s filename=\"%s\" entries: IFD0(%d) IFD1(%d) Exif(%d) GPS(%d) Interoperability(%d) MakerNote(%d)>", self.class, @fpath, @IFD0.length, @IFD1.length, @Exif.length, @GPS.length, @Interoperability.length, @MakerNote.length)
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# return true if specified tagid is defined or has some value.
|
124
|
+
#
|
125
|
+
def tag?(tagid)
|
126
|
+
search_tag(tagid) ? true : false
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# search tag on the specific IFD
|
131
|
+
#
|
132
|
+
def tag(tagname, ifd=nil)
|
133
|
+
search_tag(tagname, ifd)
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# search the specified tag values. return value is object of
|
138
|
+
# classes defined under Exif::Tag module.
|
139
|
+
#
|
140
|
+
def [](tagname)
|
141
|
+
self.tag(tagname)
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# set the specified tag to the specified value.
|
146
|
+
# XXX NOT IMPLEMETED XXX
|
147
|
+
#
|
148
|
+
def []=(tag, value)
|
149
|
+
# not implemented
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# extract the thumbnail image to dest. dest should respond to
|
154
|
+
# '<<' method.
|
155
|
+
#
|
156
|
+
def thumbnail(dest)
|
157
|
+
dest << @thumbnail
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# return the size of the thumbnail image
|
162
|
+
#
|
163
|
+
def thumbnail_size
|
164
|
+
@thumbnail.size
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# return all the tags in the image.
|
169
|
+
#
|
170
|
+
# if argument ifd is specified, every tags defined in the
|
171
|
+
# specified IFD are passed to block.
|
172
|
+
#
|
173
|
+
# return value is object of classes defined under Exif::Tag module.
|
174
|
+
#
|
175
|
+
# allowable arguments are:
|
176
|
+
# * :IFD0
|
177
|
+
# * :IFD1
|
178
|
+
# * :Exif
|
179
|
+
# * :GPS
|
180
|
+
# * :Interoperability
|
181
|
+
# * :MakerNote (if exist)
|
182
|
+
def tags(ifd=nil)
|
183
|
+
if ifd
|
184
|
+
@scanner.result[ifd]
|
185
|
+
else
|
186
|
+
[
|
187
|
+
@IFD0,
|
188
|
+
@IFD1,
|
189
|
+
@Exif,
|
190
|
+
@GPS,
|
191
|
+
@Interoperability,
|
192
|
+
@MakerNote
|
193
|
+
].flatten
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# execute given block with block argument being every tags defined
|
199
|
+
# in all the IFDs contained in the image.
|
200
|
+
#
|
201
|
+
# if argument ifd is specified, every tags defined in the
|
202
|
+
# specified IFD are passed to block.
|
203
|
+
#
|
204
|
+
# return value is object of classes defined under Exif::Tag module.
|
205
|
+
#
|
206
|
+
# allowable arguments are:
|
207
|
+
# * :IFD0
|
208
|
+
# * :IFD1
|
209
|
+
# * :Exif
|
210
|
+
# * :GPS
|
211
|
+
# * :Interoperability
|
212
|
+
# * :MakerNote
|
213
|
+
def each(ifd=nil)
|
214
|
+
if ifd
|
215
|
+
@scanner.result[ifd].each{ |tag| yield tag }
|
216
|
+
else
|
217
|
+
[
|
218
|
+
@IFD0,
|
219
|
+
@IFD1,
|
220
|
+
@Exif,
|
221
|
+
@GPS,
|
222
|
+
@Interoperability,
|
223
|
+
@MakerNote
|
224
|
+
].flatten.each do |tag|
|
225
|
+
yield tag
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def search_tag(tagID, ifd=nil)
|
233
|
+
if ifd
|
234
|
+
@scanner.result(ifd).find do |tag|
|
235
|
+
case tagID
|
236
|
+
when Fixnum
|
237
|
+
tag.tagID.hex == tagID
|
238
|
+
when String
|
239
|
+
tag.name == tagID
|
240
|
+
end
|
241
|
+
end
|
242
|
+
else
|
243
|
+
[
|
244
|
+
@IFD0,
|
245
|
+
@IFD1,
|
246
|
+
@Exif,
|
247
|
+
@GPS,
|
248
|
+
@Interoperability,
|
249
|
+
@MakerNote
|
250
|
+
].flatten.find do |tag|
|
251
|
+
case tagID
|
252
|
+
when Fixnum
|
253
|
+
tag.tagID.hex == tagID
|
254
|
+
when String
|
255
|
+
tag.name == tagID
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
end # module Parser
|
262
|
+
|
263
|
+
end # module Exif
|
264
|
+
|
265
|
+
ExifParser = Exif::Parser
|
@@ -0,0 +1,502 @@
|
|
1
|
+
#
|
2
|
+
# exifparser/makernote/nikon.rb -
|
3
|
+
#
|
4
|
+
# Copyright (C) 2002 Ryuichi Tamura (r-tam@fsinet.or.jp)
|
5
|
+
#
|
6
|
+
# $Revision: 1.1.1.1 $
|
7
|
+
# $Date: 2002/12/16 07:59:00 $
|
8
|
+
#
|
9
|
+
require 'exifparser/tag'
|
10
|
+
require 'exifparser/utils'
|
11
|
+
|
12
|
+
module Exif
|
13
|
+
|
14
|
+
module Tag
|
15
|
+
|
16
|
+
module MakerNote
|
17
|
+
|
18
|
+
#
|
19
|
+
# 0x0000 - Unknown
|
20
|
+
#
|
21
|
+
|
22
|
+
#
|
23
|
+
# 0x0001 - Tag0x0001
|
24
|
+
#
|
25
|
+
class Tag0x0001 < Base
|
26
|
+
|
27
|
+
def processData
|
28
|
+
@formatted = []
|
29
|
+
partition_data(@count) do |part|
|
30
|
+
@formatted.push _formatData(part)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def value
|
35
|
+
numTags = @formatted[0] / 2
|
36
|
+
ret = {}
|
37
|
+
|
38
|
+
return ret if numTags < 2
|
39
|
+
#
|
40
|
+
# offset 1 : Macro mode
|
41
|
+
#
|
42
|
+
ret["Macro mode"] =
|
43
|
+
case @formatted[1]
|
44
|
+
when 1
|
45
|
+
"Macro"
|
46
|
+
when 2
|
47
|
+
"Normal"
|
48
|
+
else
|
49
|
+
"Unknown"
|
50
|
+
end
|
51
|
+
|
52
|
+
return ret if numTags < 3
|
53
|
+
#
|
54
|
+
# offset 2 : if nonzero, length of self-timer in 10ths of a second.
|
55
|
+
#
|
56
|
+
selftimer_length = @formatted[2]
|
57
|
+
|
58
|
+
return ret if numTags < 5
|
59
|
+
#
|
60
|
+
# offset 4 : Flash mode
|
61
|
+
#
|
62
|
+
ret["Flash mode"] =
|
63
|
+
case @formatted[4]
|
64
|
+
when 0
|
65
|
+
"flash not fired"
|
66
|
+
when 1
|
67
|
+
"auto"
|
68
|
+
when 2
|
69
|
+
"on"
|
70
|
+
when 3
|
71
|
+
"red-eye reduction"
|
72
|
+
when 4
|
73
|
+
"slow synchro"
|
74
|
+
when 5
|
75
|
+
"auto + redeye reduction"
|
76
|
+
when 6
|
77
|
+
"on + redeye reduction"
|
78
|
+
when 16
|
79
|
+
"external flash"
|
80
|
+
else
|
81
|
+
"unknown"
|
82
|
+
end
|
83
|
+
|
84
|
+
return ret if numTags < 6
|
85
|
+
#
|
86
|
+
# offset 5: Contiuous drive mode
|
87
|
+
#
|
88
|
+
ret["Continuous drive mode"] =
|
89
|
+
case @formatted[5]
|
90
|
+
when 0
|
91
|
+
if selftimer_length != 0
|
92
|
+
"Timer = #{selftimer_length/10.0}sec."
|
93
|
+
else
|
94
|
+
"Single"
|
95
|
+
end
|
96
|
+
when 1
|
97
|
+
"Continuous"
|
98
|
+
end
|
99
|
+
|
100
|
+
return ret if numTags < 8
|
101
|
+
#
|
102
|
+
# offset 7: Focus Mode
|
103
|
+
#
|
104
|
+
ret["Focus Mode"] =
|
105
|
+
case @formatted[7]
|
106
|
+
when 0
|
107
|
+
"One-Shot"
|
108
|
+
when 1
|
109
|
+
"AI Servo"
|
110
|
+
when 2
|
111
|
+
"AI Focus"
|
112
|
+
when 3
|
113
|
+
"MF"
|
114
|
+
when 4
|
115
|
+
"Single"
|
116
|
+
when 5
|
117
|
+
"Continuous"
|
118
|
+
when 6
|
119
|
+
"MF"
|
120
|
+
else
|
121
|
+
"Unknown"
|
122
|
+
end
|
123
|
+
|
124
|
+
return ret if numTags < 11
|
125
|
+
#
|
126
|
+
# offset 10: Image size
|
127
|
+
#
|
128
|
+
ret["Image Size"] =
|
129
|
+
case @formatted[10]
|
130
|
+
when 0
|
131
|
+
"Large"
|
132
|
+
when 1
|
133
|
+
"Medium"
|
134
|
+
when
|
135
|
+
"Small"
|
136
|
+
else
|
137
|
+
"Unknown"
|
138
|
+
end
|
139
|
+
|
140
|
+
return ret if numTags < 12
|
141
|
+
#
|
142
|
+
# offset 11: "Easy shooting" mode
|
143
|
+
#
|
144
|
+
ret["Easy shooting mode"] =
|
145
|
+
case @formatted[11]
|
146
|
+
when 0
|
147
|
+
"Full auto"
|
148
|
+
when 1
|
149
|
+
"Manual"
|
150
|
+
when 2
|
151
|
+
"Landscape"
|
152
|
+
when 3
|
153
|
+
"Fast Shutter"
|
154
|
+
when 4
|
155
|
+
"Slow Shutter"
|
156
|
+
when 5
|
157
|
+
"Night"
|
158
|
+
when 6
|
159
|
+
"B&W"
|
160
|
+
when 7
|
161
|
+
"Sepia"
|
162
|
+
when 8
|
163
|
+
"Portrait"
|
164
|
+
when 9
|
165
|
+
"Sports"
|
166
|
+
when 10
|
167
|
+
"Macro / Close-Up"
|
168
|
+
when 11
|
169
|
+
"Pan Focus"
|
170
|
+
else
|
171
|
+
"Unknown"
|
172
|
+
end
|
173
|
+
|
174
|
+
return ret if numTags < 14
|
175
|
+
#
|
176
|
+
# offset 13: Contrast
|
177
|
+
#
|
178
|
+
ret["Contrast"] =
|
179
|
+
case @formatted[13]
|
180
|
+
when 0xffff
|
181
|
+
"Low"
|
182
|
+
when 0x0000
|
183
|
+
"Normal"
|
184
|
+
when 0x0001
|
185
|
+
"High"
|
186
|
+
else
|
187
|
+
"Unknown"
|
188
|
+
end
|
189
|
+
|
190
|
+
return ret if numTags < 15
|
191
|
+
#
|
192
|
+
# offset 14: Saturation
|
193
|
+
#
|
194
|
+
ret["Saturation"] =
|
195
|
+
case @formatted[14]
|
196
|
+
when 0xffff
|
197
|
+
"Low"
|
198
|
+
when 0x0000
|
199
|
+
"Normal"
|
200
|
+
when 0x0001
|
201
|
+
"High"
|
202
|
+
else
|
203
|
+
"Unknown"
|
204
|
+
end
|
205
|
+
|
206
|
+
return ret if numTags < 16
|
207
|
+
#
|
208
|
+
# offset 15: Contrast
|
209
|
+
#
|
210
|
+
ret["Sharpness"] =
|
211
|
+
case @formatted[15]
|
212
|
+
when 0xffff
|
213
|
+
"Low"
|
214
|
+
when 0x0000
|
215
|
+
"Normal"
|
216
|
+
when 0x0001
|
217
|
+
"High"
|
218
|
+
else
|
219
|
+
"Unknown"
|
220
|
+
end
|
221
|
+
|
222
|
+
return ret if numTags < 17
|
223
|
+
#
|
224
|
+
# offset 16: ISO
|
225
|
+
#
|
226
|
+
ret["ISO"] =
|
227
|
+
case @formatted[16]
|
228
|
+
when 0
|
229
|
+
"ISOSpeedRatings"
|
230
|
+
when 15
|
231
|
+
"Auto"
|
232
|
+
when 16
|
233
|
+
50
|
234
|
+
when 17
|
235
|
+
100
|
236
|
+
when 18
|
237
|
+
200
|
238
|
+
when 19
|
239
|
+
400
|
240
|
+
else
|
241
|
+
"Unknown"
|
242
|
+
end
|
243
|
+
|
244
|
+
return ret if numTags < 18
|
245
|
+
#
|
246
|
+
# offset 17: Metering mode
|
247
|
+
#
|
248
|
+
ret['Metering mode'] =
|
249
|
+
case @formatted[17]
|
250
|
+
when 3
|
251
|
+
"Evaluative"
|
252
|
+
when 4
|
253
|
+
"Partial"
|
254
|
+
when 5
|
255
|
+
"Center-weighted"
|
256
|
+
else
|
257
|
+
"Unknown"
|
258
|
+
end
|
259
|
+
ret
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
#
|
265
|
+
# 0x0003 - Tag0x0003
|
266
|
+
#
|
267
|
+
class Tag0x0003 < Base
|
268
|
+
end
|
269
|
+
|
270
|
+
#
|
271
|
+
# 0x0004 - Tag0x0004
|
272
|
+
#
|
273
|
+
class Tag0x0004 < Base
|
274
|
+
|
275
|
+
def processData
|
276
|
+
@formatted = []
|
277
|
+
partition_data(@count) do |part|
|
278
|
+
@formatted.push _formatData(part)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def value
|
283
|
+
numTags = @formatted[0] / 2
|
284
|
+
ret = {}
|
285
|
+
|
286
|
+
return hash if numTags < 8
|
287
|
+
# offset 7 : white balance
|
288
|
+
ret['White balance'] =
|
289
|
+
case @formatted[7]
|
290
|
+
when 0
|
291
|
+
"Auto"
|
292
|
+
when 1
|
293
|
+
"Sunny"
|
294
|
+
when 2
|
295
|
+
"Cloudy"
|
296
|
+
when 3
|
297
|
+
"Tungsten"
|
298
|
+
when 4
|
299
|
+
"Florescent"
|
300
|
+
when 5
|
301
|
+
"Flash"
|
302
|
+
when 6
|
303
|
+
"Custom"
|
304
|
+
else
|
305
|
+
"Unknown"
|
306
|
+
end
|
307
|
+
|
308
|
+
return ret if numTags < 10
|
309
|
+
# offset 9: Sequence number (if in a continuous burst)
|
310
|
+
ret['Sequence number'] = @formatted[9]
|
311
|
+
|
312
|
+
return ret if numTags < 15
|
313
|
+
# offset 14: Auto Focus point used
|
314
|
+
ret['Auto Focus point used'] = @formatted[14]
|
315
|
+
|
316
|
+
return ret if numTags < 16
|
317
|
+
ret['Flash bias'] =
|
318
|
+
case @formatted[15]
|
319
|
+
when 0xffc0
|
320
|
+
"-2 EV"
|
321
|
+
when 0xffcc
|
322
|
+
"-1.67 EV"
|
323
|
+
when 0xffd0
|
324
|
+
"-1.50 EV"
|
325
|
+
when 0xffd4
|
326
|
+
"-1.33 EV"
|
327
|
+
when 0xffe0
|
328
|
+
"-1 EV"
|
329
|
+
when 0xffec
|
330
|
+
"-0.67 EV"
|
331
|
+
when 0xfff0
|
332
|
+
"-0.50 EV"
|
333
|
+
when 0xfff4
|
334
|
+
"-0.33 EV"
|
335
|
+
when 0x0000
|
336
|
+
"0 EV"
|
337
|
+
when 0x000c
|
338
|
+
"0.33 EV"
|
339
|
+
when 0x0010
|
340
|
+
"0.50 EV"
|
341
|
+
when 0x0014
|
342
|
+
"0.67 EV"
|
343
|
+
when 0x0020
|
344
|
+
"1 EV"
|
345
|
+
when 0x002c
|
346
|
+
"1.33 EV"
|
347
|
+
when 0x0030
|
348
|
+
"1.50 EV"
|
349
|
+
when 0x0034
|
350
|
+
"1.67 EV"
|
351
|
+
when 0x0040
|
352
|
+
"2 EV"
|
353
|
+
else
|
354
|
+
"Unknown"
|
355
|
+
end
|
356
|
+
|
357
|
+
return ret if numTags < 20
|
358
|
+
ret['Subject Distance'] = @formatted[19]
|
359
|
+
|
360
|
+
ret
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
365
|
+
#
|
366
|
+
# 0x0006 - ImageType
|
367
|
+
#
|
368
|
+
class ImageType < Base
|
369
|
+
end
|
370
|
+
|
371
|
+
#
|
372
|
+
# 0x0007 - FirmwareVersion
|
373
|
+
#
|
374
|
+
class FirmwareVersion < Base
|
375
|
+
end
|
376
|
+
|
377
|
+
#
|
378
|
+
# 0x0008 - ImageNumber
|
379
|
+
#
|
380
|
+
class ImageNumber < Base
|
381
|
+
end
|
382
|
+
|
383
|
+
#
|
384
|
+
# 0x0009 - OwnerName
|
385
|
+
#
|
386
|
+
class OwnerName < Base
|
387
|
+
end
|
388
|
+
|
389
|
+
#
|
390
|
+
# 0x000a - Unknown
|
391
|
+
#
|
392
|
+
|
393
|
+
#
|
394
|
+
# 0x000c - CameraSerialNumber
|
395
|
+
#
|
396
|
+
class CameraSerialNumber < Base
|
397
|
+
|
398
|
+
def to_s
|
399
|
+
hi = @formatted / 0x10000
|
400
|
+
low = @formatted % 0x10000
|
401
|
+
"%04X%05d"%[hi, low]
|
402
|
+
end
|
403
|
+
|
404
|
+
end
|
405
|
+
|
406
|
+
#
|
407
|
+
# 0x000d - Unknown
|
408
|
+
#
|
409
|
+
|
410
|
+
#
|
411
|
+
# 0x000f - CustomFunctions
|
412
|
+
#
|
413
|
+
class CustomFunctions < Base
|
414
|
+
|
415
|
+
def processData
|
416
|
+
@formatted = []
|
417
|
+
partition_data(@count) do |part|
|
418
|
+
@formatted.push _formatData(part)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
end
|
423
|
+
|
424
|
+
end
|
425
|
+
|
426
|
+
CanonIFDTable = {
|
427
|
+
0x0000 => Unknown,
|
428
|
+
0x0001 => MakerNote::Tag0x0001,
|
429
|
+
0x0003 => MakerNote::Tag0x0003,
|
430
|
+
0x0004 => MakerNote::Tag0x0004,
|
431
|
+
0x0006 => MakerNote::ImageType,
|
432
|
+
0x0007 => MakerNote::FirmwareVersion,
|
433
|
+
0x0008 => MakerNote::ImageNumber,
|
434
|
+
0x0009 => MakerNote::OwnerName,
|
435
|
+
0x000a => Unknown,
|
436
|
+
0x000c => MakerNote::CameraSerialNumber,
|
437
|
+
0x000d => Unknown,
|
438
|
+
0x000f => MakerNote::CustomFunctions
|
439
|
+
}
|
440
|
+
|
441
|
+
end
|
442
|
+
|
443
|
+
class Canon
|
444
|
+
|
445
|
+
def initialize(fin, tiff_origin, dataPos, byteOrder_module)
|
446
|
+
@fin = fin
|
447
|
+
@tiffHeader0 = tiff_origin
|
448
|
+
@dataPos = dataPos
|
449
|
+
@byteOrder_module = byteOrder_module
|
450
|
+
self.extend @byteOrder_module
|
451
|
+
end
|
452
|
+
|
453
|
+
def scan_IFD
|
454
|
+
#
|
455
|
+
# Canon MakerNote starts from 0
|
456
|
+
#
|
457
|
+
@fin.pos = @dataPos + 0
|
458
|
+
#
|
459
|
+
# get the number of tags
|
460
|
+
#
|
461
|
+
numDirs = decode_ushort(fin_read_n(2))
|
462
|
+
#
|
463
|
+
# now scan them
|
464
|
+
#
|
465
|
+
1.upto(numDirs) {
|
466
|
+
curpos_tag = @fin.pos
|
467
|
+
tag = parseTagID(fin_read_n(2))
|
468
|
+
tagclass = Tag.find(tag.hex, Tag::CanonIFDTable)
|
469
|
+
unit, formatter = Tag::Format::Unit[decode_ushort(fin_read_n(2))]
|
470
|
+
count = decode_ulong(fin_read_n(4))
|
471
|
+
tagdata = fin_read_n(4)
|
472
|
+
obj = tagclass.new(tag, "MakerNote", count)
|
473
|
+
obj.extend formatter, @byteOrder_module
|
474
|
+
obj.pos = curpos_tag
|
475
|
+
if unit * count > 4
|
476
|
+
curpos = @fin.pos
|
477
|
+
begin
|
478
|
+
@fin.pos = @tiffHeader0 + decode_ulong(tagdata)
|
479
|
+
obj.dataPos = @fin.pos
|
480
|
+
obj.data = fin_read_n(unit*count)
|
481
|
+
ensure
|
482
|
+
@fin.pos = curpos
|
483
|
+
end
|
484
|
+
else
|
485
|
+
obj.dataPos = @fin.pos - 4
|
486
|
+
obj.data = tagdata
|
487
|
+
end
|
488
|
+
obj.processData
|
489
|
+
yield obj
|
490
|
+
}
|
491
|
+
end
|
492
|
+
|
493
|
+
private
|
494
|
+
|
495
|
+
def fin_read_n(n)
|
496
|
+
@fin.read(n)
|
497
|
+
end
|
498
|
+
|
499
|
+
end
|
500
|
+
|
501
|
+
|
502
|
+
end
|