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.
@@ -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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in exifparser.gemspec
4
+ gemspec
@@ -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.
@@ -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
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -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
@@ -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