exifparser 1.0.0
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.
- 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
|