multi_exiftool 0.0.1
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.
- data/CHANGELOG +1 -0
- data/COPYING +165 -0
- data/Manifest +25 -0
- data/README +29 -0
- data/Rakefile +43 -0
- data/data/fixtures/read_non_existing_file.stderr +1 -0
- data/data/fixtures/read_non_existing_file.stdout +0 -0
- data/data/fixtures/read_one_file.stderr +0 -0
- data/data/fixtures/read_one_file.stdout +92 -0
- data/data/fixtures/read_two_files.stderr +0 -0
- data/data/fixtures/read_two_files.stdout +212 -0
- data/data/regression/read_command.rb +12 -0
- data/data/regression/read_command.rb.out +16 -0
- data/data/regression/write_command.rb +16 -0
- data/data/regression/write_command.rb.out +40 -0
- data/lib/multi_exiftool.rb +43 -0
- data/lib/multi_exiftool/command_generator.rb +68 -0
- data/lib/multi_exiftool/parser.rb +43 -0
- data/lib/multi_exiftool/read_object.rb +57 -0
- data/multi_exiftool.gemspec +35 -0
- data/script/colorize.rb +6 -0
- data/script/generate_fixture.rb +27 -0
- data/test/test_command_generator.rb +89 -0
- data/test/test_helper.rb +44 -0
- data/test/test_parser.rb +51 -0
- data/test/test_read_object.rb +36 -0
- metadata +101 -0
data/CHANGELOG
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
v0.0.1 experimental
|
data/COPYING
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 29 June 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
|
6
|
+
of this license document, but changing it is not allowed.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
This version of the GNU Lesser General Public License incorporates
|
|
10
|
+
the terms and conditions of version 3 of the GNU General Public
|
|
11
|
+
License, supplemented by the additional permissions listed below.
|
|
12
|
+
|
|
13
|
+
0. Additional Definitions.
|
|
14
|
+
|
|
15
|
+
As used herein, "this License" refers to version 3 of the GNU Lesser
|
|
16
|
+
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
|
17
|
+
General Public License.
|
|
18
|
+
|
|
19
|
+
"The Library" refers to a covered work governed by this License,
|
|
20
|
+
other than an Application or a Combined Work as defined below.
|
|
21
|
+
|
|
22
|
+
An "Application" is any work that makes use of an interface provided
|
|
23
|
+
by the Library, but which is not otherwise based on the Library.
|
|
24
|
+
Defining a subclass of a class defined by the Library is deemed a mode
|
|
25
|
+
of using an interface provided by the Library.
|
|
26
|
+
|
|
27
|
+
A "Combined Work" is a work produced by combining or linking an
|
|
28
|
+
Application with the Library. The particular version of the Library
|
|
29
|
+
with which the Combined Work was made is also called the "Linked
|
|
30
|
+
Version".
|
|
31
|
+
|
|
32
|
+
The "Minimal Corresponding Source" for a Combined Work means the
|
|
33
|
+
Corresponding Source for the Combined Work, excluding any source code
|
|
34
|
+
for portions of the Combined Work that, considered in isolation, are
|
|
35
|
+
based on the Application, and not on the Linked Version.
|
|
36
|
+
|
|
37
|
+
The "Corresponding Application Code" for a Combined Work means the
|
|
38
|
+
object code and/or source code for the Application, including any data
|
|
39
|
+
and utility programs needed for reproducing the Combined Work from the
|
|
40
|
+
Application, but excluding the System Libraries of the Combined Work.
|
|
41
|
+
|
|
42
|
+
1. Exception to Section 3 of the GNU GPL.
|
|
43
|
+
|
|
44
|
+
You may convey a covered work under sections 3 and 4 of this License
|
|
45
|
+
without being bound by section 3 of the GNU GPL.
|
|
46
|
+
|
|
47
|
+
2. Conveying Modified Versions.
|
|
48
|
+
|
|
49
|
+
If you modify a copy of the Library, and, in your modifications, a
|
|
50
|
+
facility refers to a function or data to be supplied by an Application
|
|
51
|
+
that uses the facility (other than as an argument passed when the
|
|
52
|
+
facility is invoked), then you may convey a copy of the modified
|
|
53
|
+
version:
|
|
54
|
+
|
|
55
|
+
a) under this License, provided that you make a good faith effort to
|
|
56
|
+
ensure that, in the event an Application does not supply the
|
|
57
|
+
function or data, the facility still operates, and performs
|
|
58
|
+
whatever part of its purpose remains meaningful, or
|
|
59
|
+
|
|
60
|
+
b) under the GNU GPL, with none of the additional permissions of
|
|
61
|
+
this License applicable to that copy.
|
|
62
|
+
|
|
63
|
+
3. Object Code Incorporating Material from Library Header Files.
|
|
64
|
+
|
|
65
|
+
The object code form of an Application may incorporate material from
|
|
66
|
+
a header file that is part of the Library. You may convey such object
|
|
67
|
+
code under terms of your choice, provided that, if the incorporated
|
|
68
|
+
material is not limited to numerical parameters, data structure
|
|
69
|
+
layouts and accessors, or small macros, inline functions and templates
|
|
70
|
+
(ten or fewer lines in length), you do both of the following:
|
|
71
|
+
|
|
72
|
+
a) Give prominent notice with each copy of the object code that the
|
|
73
|
+
Library is used in it and that the Library and its use are
|
|
74
|
+
covered by this License.
|
|
75
|
+
|
|
76
|
+
b) Accompany the object code with a copy of the GNU GPL and this license
|
|
77
|
+
document.
|
|
78
|
+
|
|
79
|
+
4. Combined Works.
|
|
80
|
+
|
|
81
|
+
You may convey a Combined Work under terms of your choice that,
|
|
82
|
+
taken together, effectively do not restrict modification of the
|
|
83
|
+
portions of the Library contained in the Combined Work and reverse
|
|
84
|
+
engineering for debugging such modifications, if you also do each of
|
|
85
|
+
the following:
|
|
86
|
+
|
|
87
|
+
a) Give prominent notice with each copy of the Combined Work that
|
|
88
|
+
the Library is used in it and that the Library and its use are
|
|
89
|
+
covered by this License.
|
|
90
|
+
|
|
91
|
+
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
|
92
|
+
document.
|
|
93
|
+
|
|
94
|
+
c) For a Combined Work that displays copyright notices during
|
|
95
|
+
execution, include the copyright notice for the Library among
|
|
96
|
+
these notices, as well as a reference directing the user to the
|
|
97
|
+
copies of the GNU GPL and this license document.
|
|
98
|
+
|
|
99
|
+
d) Do one of the following:
|
|
100
|
+
|
|
101
|
+
0) Convey the Minimal Corresponding Source under the terms of this
|
|
102
|
+
License, and the Corresponding Application Code in a form
|
|
103
|
+
suitable for, and under terms that permit, the user to
|
|
104
|
+
recombine or relink the Application with a modified version of
|
|
105
|
+
the Linked Version to produce a modified Combined Work, in the
|
|
106
|
+
manner specified by section 6 of the GNU GPL for conveying
|
|
107
|
+
Corresponding Source.
|
|
108
|
+
|
|
109
|
+
1) Use a suitable shared library mechanism for linking with the
|
|
110
|
+
Library. A suitable mechanism is one that (a) uses at run time
|
|
111
|
+
a copy of the Library already present on the user's computer
|
|
112
|
+
system, and (b) will operate properly with a modified version
|
|
113
|
+
of the Library that is interface-compatible with the Linked
|
|
114
|
+
Version.
|
|
115
|
+
|
|
116
|
+
e) Provide Installation Information, but only if you would otherwise
|
|
117
|
+
be required to provide such information under section 6 of the
|
|
118
|
+
GNU GPL, and only to the extent that such information is
|
|
119
|
+
necessary to install and execute a modified version of the
|
|
120
|
+
Combined Work produced by recombining or relinking the
|
|
121
|
+
Application with a modified version of the Linked Version. (If
|
|
122
|
+
you use option 4d0, the Installation Information must accompany
|
|
123
|
+
the Minimal Corresponding Source and Corresponding Application
|
|
124
|
+
Code. If you use option 4d1, you must provide the Installation
|
|
125
|
+
Information in the manner specified by section 6 of the GNU GPL
|
|
126
|
+
for conveying Corresponding Source.)
|
|
127
|
+
|
|
128
|
+
5. Combined Libraries.
|
|
129
|
+
|
|
130
|
+
You may place library facilities that are a work based on the
|
|
131
|
+
Library side by side in a single library together with other library
|
|
132
|
+
facilities that are not Applications and are not covered by this
|
|
133
|
+
License, and convey such a combined library under terms of your
|
|
134
|
+
choice, if you do both of the following:
|
|
135
|
+
|
|
136
|
+
a) Accompany the combined library with a copy of the same work based
|
|
137
|
+
on the Library, uncombined with any other library facilities,
|
|
138
|
+
conveyed under the terms of this License.
|
|
139
|
+
|
|
140
|
+
b) Give prominent notice with the combined library that part of it
|
|
141
|
+
is a work based on the Library, and explaining where to find the
|
|
142
|
+
accompanying uncombined form of the same work.
|
|
143
|
+
|
|
144
|
+
6. Revised Versions of the GNU Lesser General Public License.
|
|
145
|
+
|
|
146
|
+
The Free Software Foundation may publish revised and/or new versions
|
|
147
|
+
of the GNU Lesser General Public License from time to time. Such new
|
|
148
|
+
versions will be similar in spirit to the present version, but may
|
|
149
|
+
differ in detail to address new problems or concerns.
|
|
150
|
+
|
|
151
|
+
Each version is given a distinguishing version number. If the
|
|
152
|
+
Library as you received it specifies that a certain numbered version
|
|
153
|
+
of the GNU Lesser General Public License "or any later version"
|
|
154
|
+
applies to it, you have the option of following the terms and
|
|
155
|
+
conditions either of that published version or of any later version
|
|
156
|
+
published by the Free Software Foundation. If the Library as you
|
|
157
|
+
received it does not specify a version number of the GNU Lesser
|
|
158
|
+
General Public License, you may choose any version of the GNU Lesser
|
|
159
|
+
General Public License ever published by the Free Software Foundation.
|
|
160
|
+
|
|
161
|
+
If the Library as you received it specifies that a proxy can decide
|
|
162
|
+
whether future versions of the GNU Lesser General Public License shall
|
|
163
|
+
apply, that proxy's public statement of acceptance of any version is
|
|
164
|
+
permanent authorization for you to choose that version for the
|
|
165
|
+
Library.
|
data/Manifest
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Rakefile
|
|
2
|
+
Manifest
|
|
3
|
+
test/test_command_generator.rb
|
|
4
|
+
test/test_helper.rb
|
|
5
|
+
test/test_parser.rb
|
|
6
|
+
test/test_read_object.rb
|
|
7
|
+
lib/multi_exiftool.rb
|
|
8
|
+
lib/multi_exiftool/parser.rb
|
|
9
|
+
lib/multi_exiftool/command_generator.rb
|
|
10
|
+
lib/multi_exiftool/read_object.rb
|
|
11
|
+
README
|
|
12
|
+
data/fixtures/read_non_existing_file.stderr
|
|
13
|
+
data/fixtures/read_non_existing_file.stdout
|
|
14
|
+
data/fixtures/read_two_files.stderr
|
|
15
|
+
data/fixtures/read_one_file.stderr
|
|
16
|
+
data/fixtures/read_one_file.stdout
|
|
17
|
+
data/fixtures/read_two_files.stdout
|
|
18
|
+
data/regression/read_command.rb
|
|
19
|
+
data/regression/read_command.rb.out
|
|
20
|
+
data/regression/write_command.rb
|
|
21
|
+
data/regression/write_command.rb.out
|
|
22
|
+
script/generate_fixture.rb
|
|
23
|
+
script/colorize.rb
|
|
24
|
+
CHANGELOG
|
|
25
|
+
COPYING
|
data/README
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
= MULTI EXIFTOOL
|
|
2
|
+
Jan Friedrich
|
|
3
|
+
http://multi_exiftool.rubyforge.org
|
|
4
|
+
|
|
5
|
+
== DESCRIPTION:
|
|
6
|
+
|
|
7
|
+
FIXME (describe your package)
|
|
8
|
+
|
|
9
|
+
== FEATURES/PROBLEMS:
|
|
10
|
+
|
|
11
|
+
* FIXME (list of features or problems)
|
|
12
|
+
|
|
13
|
+
== SYNOPSIS:
|
|
14
|
+
|
|
15
|
+
FIXME (code sample of usage)
|
|
16
|
+
|
|
17
|
+
== REQUIREMENTS:
|
|
18
|
+
|
|
19
|
+
* FIXME (list of requirements)
|
|
20
|
+
|
|
21
|
+
== INSTALL:
|
|
22
|
+
|
|
23
|
+
* FIXME (sudo gem install, anything else)
|
|
24
|
+
|
|
25
|
+
== LICENSE:
|
|
26
|
+
|
|
27
|
+
GNU Lesser General Public License (LGPL)
|
|
28
|
+
|
|
29
|
+
See file COPYING.txt for more details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'echoe'
|
|
2
|
+
|
|
3
|
+
Echoe.new 'multi_exiftool' do |p|
|
|
4
|
+
p.author = 'Jan Friedrich'
|
|
5
|
+
p.email = 'janfri.rubyforge@gmail.com'
|
|
6
|
+
p.project = 'multiexiftool'
|
|
7
|
+
p.url = 'http://multiexiftool.rubyforge.org'
|
|
8
|
+
p.development_dependencies = ['yact']
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
namespace :reg do
|
|
12
|
+
|
|
13
|
+
ROOT_DIR = File.dirname(__FILE__)
|
|
14
|
+
SCRIPT_DIR = File.join(ROOT_DIR, 'script')
|
|
15
|
+
DATA_DIR = File.join(ROOT_DIR, %w(data regression))
|
|
16
|
+
RB_FILES = File.join(DATA_DIR, '*.rb')
|
|
17
|
+
|
|
18
|
+
desc 'Generate regression data'
|
|
19
|
+
task :generate
|
|
20
|
+
|
|
21
|
+
def target src
|
|
22
|
+
src + '.out'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
FileList[RB_FILES].each do |src|
|
|
26
|
+
target = target(src)
|
|
27
|
+
file target => src do |t|
|
|
28
|
+
sh "ruby #{t.prerequisites.first} > #{t.name}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
task :generate => target
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc 'Running regression tests'
|
|
35
|
+
task :test => :generate do
|
|
36
|
+
FileList[RB_FILES].each do |src|
|
|
37
|
+
target = target(src)
|
|
38
|
+
puts '-' * 8 << File.basename(src)
|
|
39
|
+
sh "ruby #{src} | wdiff -n #{target} - | #{File.join(SCRIPT_DIR, 'colorize.rb')}"# ; echo x >/dev/null"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
File not found: non_existing_file
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
ExifToolVersion 7.03
|
|
2
|
+
FileName sea.jpg
|
|
3
|
+
Directory /home/janfri
|
|
4
|
+
FileSize 3 MB
|
|
5
|
+
FileModifyDate 2008:05:21 20:59:44
|
|
6
|
+
FileType JPEG
|
|
7
|
+
MIMEType image/jpeg
|
|
8
|
+
ExifByteOrder Big-endian (Motorola)
|
|
9
|
+
ImageDescription KONICA MINOLTA DIGITAL CAMERA
|
|
10
|
+
Make KONICA MINOLTA
|
|
11
|
+
Model DYNAX 7D
|
|
12
|
+
Orientation Horizontal (normal)
|
|
13
|
+
XResolution 72
|
|
14
|
+
YResolution 72
|
|
15
|
+
ResolutionUnit inches
|
|
16
|
+
Software DYNAX 7D v1.10
|
|
17
|
+
ModifyDate 2005:10:06 15:41:08
|
|
18
|
+
YCbCrPositioning Centered
|
|
19
|
+
ExposureTime 1/200
|
|
20
|
+
FNumber 9.0
|
|
21
|
+
ExposureProgram Program AE
|
|
22
|
+
ISO 100
|
|
23
|
+
ExifVersion 0221
|
|
24
|
+
DateTimeOriginal 2005:10:06 15:41:08
|
|
25
|
+
CreateDate 2005:10:06 15:41:08
|
|
26
|
+
ComponentsConfiguration YCbCr
|
|
27
|
+
BrightnessValue 9
|
|
28
|
+
ExposureCompensation 0
|
|
29
|
+
MaxApertureValue 3.5
|
|
30
|
+
MeteringMode Multi-segment
|
|
31
|
+
LightSource Unknown (0)
|
|
32
|
+
Flash Off
|
|
33
|
+
FocalLength 28.0mm
|
|
34
|
+
SubjectLocation 1504 1000 256 304
|
|
35
|
+
MakerNoteVersion MLT0
|
|
36
|
+
MinoltaImageSize Large
|
|
37
|
+
WhiteBalance Auto
|
|
38
|
+
FocusMode Manual
|
|
39
|
+
AFPoints Center
|
|
40
|
+
ISOSetting 100
|
|
41
|
+
FreeMemoryCardImages 127
|
|
42
|
+
Rotation Horizontal (normal)
|
|
43
|
+
ImageNumber 6
|
|
44
|
+
NoiseReduction Unknown (2)
|
|
45
|
+
ImageNumber2 60
|
|
46
|
+
ZoneMatchingOn Off
|
|
47
|
+
CompressedImageSize 2937828
|
|
48
|
+
PreviewImageStart 2985987
|
|
49
|
+
PreviewImageLength 36807
|
|
50
|
+
SceneMode Standard
|
|
51
|
+
ColorMode Natural sRGB
|
|
52
|
+
MinoltaQuality Fine
|
|
53
|
+
FlashExposureComp 0
|
|
54
|
+
ImageStabilization On
|
|
55
|
+
ZoneMatching ISO Setting Used
|
|
56
|
+
ColorTemperature 0
|
|
57
|
+
LensID Tamron 18-200, 28-300 or 80-300mm F3.5-6.3
|
|
58
|
+
UserComment
|
|
59
|
+
FlashpixVersion 0100
|
|
60
|
+
ColorSpace sRGB
|
|
61
|
+
ExifImageWidth 3008
|
|
62
|
+
ExifImageLength 2000
|
|
63
|
+
InteropIndex R98 - DCF basic file (sRGB)
|
|
64
|
+
InteropVersion 0100
|
|
65
|
+
CustomRendered Normal
|
|
66
|
+
ExposureMode Auto
|
|
67
|
+
DigitalZoomRatio 0
|
|
68
|
+
FocalLengthIn35mmFormat 42mm
|
|
69
|
+
SceneCaptureType Standard
|
|
70
|
+
GainControl None
|
|
71
|
+
Contrast Normal
|
|
72
|
+
Saturation Normal
|
|
73
|
+
Sharpness Normal
|
|
74
|
+
Compression JPEG (old-style)
|
|
75
|
+
ThumbnailOffset 40306
|
|
76
|
+
ThumbnailLength 3002
|
|
77
|
+
ImageWidth 3008
|
|
78
|
+
ImageHeight 2000
|
|
79
|
+
EncodingProcess Baseline DCT, Huffman coding
|
|
80
|
+
BitsPerSample 8
|
|
81
|
+
ColorComponents 3
|
|
82
|
+
YCbCrSubSampling YCbCr4:2:2 (2 1)
|
|
83
|
+
Aperture 9.0
|
|
84
|
+
ImageSize 3008x2000
|
|
85
|
+
PreviewImage (Binary data 36807 bytes, use -b option to extract)
|
|
86
|
+
ScaleFactor35efl 1.5
|
|
87
|
+
ShutterSpeed 1/200
|
|
88
|
+
ThumbnailImage (Binary data 3002 bytes, use -b option to extract)
|
|
89
|
+
CircleOfConfusion 0.020 mm
|
|
90
|
+
FocalLength35efl 28.0mm (35mm equivalent: 42.0mm)
|
|
91
|
+
HyperfocalDistance 4.35 m
|
|
92
|
+
LightValue 14.0
|
|
File without changes
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
======== /home/janfri/test_photos/2008-02-16_0013.jpe
|
|
2
|
+
ExifToolVersion 7.03
|
|
3
|
+
FileName 2008-02-16_0013.jpe
|
|
4
|
+
Directory /home/janfri/test_photos
|
|
5
|
+
FileSize 2 MB
|
|
6
|
+
FileModifyDate 2008:02:16 16:10:54
|
|
7
|
+
FileType JPEG
|
|
8
|
+
MIMEType image/jpeg
|
|
9
|
+
ExifByteOrder Big-endian (Motorola)
|
|
10
|
+
ImageDescription KONICA MINOLTA DIGITAL CAMERA
|
|
11
|
+
Make KONICA MINOLTA
|
|
12
|
+
Model DYNAX 7D
|
|
13
|
+
Orientation Horizontal (normal)
|
|
14
|
+
XResolution 72
|
|
15
|
+
YResolution 72
|
|
16
|
+
ResolutionUnit inches
|
|
17
|
+
Software DYNAX 7D v1.10
|
|
18
|
+
ModifyDate 2008:02:16 16:10:52
|
|
19
|
+
YCbCrPositioning Centered
|
|
20
|
+
ExposureTime 1/250
|
|
21
|
+
FNumber 6.7
|
|
22
|
+
ExposureProgram Aperture-priority AE
|
|
23
|
+
ISO 100
|
|
24
|
+
ExifVersion 0221
|
|
25
|
+
DateTimeOriginal 2008:02:16 16:10:52
|
|
26
|
+
CreateDate 2008:02:16 16:10:52
|
|
27
|
+
ComponentsConfiguration YCbCr
|
|
28
|
+
BrightnessValue 8.5
|
|
29
|
+
ExposureCompensation 0
|
|
30
|
+
MaxApertureValue 6.7
|
|
31
|
+
MeteringMode Center-weighted average
|
|
32
|
+
LightSource Unknown (0)
|
|
33
|
+
Flash Off
|
|
34
|
+
FocalLength 300.0mm
|
|
35
|
+
SubjectLocation 1504 1000 256 304
|
|
36
|
+
MakerNoteVersion MLT0
|
|
37
|
+
MinoltaImageSize Large
|
|
38
|
+
WhiteBalance Auto
|
|
39
|
+
FocusMode Single-shot AF
|
|
40
|
+
AFPoints Center
|
|
41
|
+
ISOSetting Auto
|
|
42
|
+
FreeMemoryCardImages 49
|
|
43
|
+
Rotation Horizontal (normal)
|
|
44
|
+
ImageNumber 6
|
|
45
|
+
NoiseReduction Unknown (2)
|
|
46
|
+
ImageNumber2 304
|
|
47
|
+
ZoneMatchingOn Off
|
|
48
|
+
CompressedImageSize 2322879
|
|
49
|
+
PreviewImageStart 2372611
|
|
50
|
+
PreviewImageLength 46166
|
|
51
|
+
SceneMode Standard
|
|
52
|
+
ColorMode Adobe RGB
|
|
53
|
+
MinoltaQuality Fine
|
|
54
|
+
FlashExposureComp 0
|
|
55
|
+
ImageStabilization On
|
|
56
|
+
ZoneMatching ISO Setting Used
|
|
57
|
+
ColorTemperature 0
|
|
58
|
+
LensID Tamron 18-200, 28-300 or 80-300mm F3.5-6.3
|
|
59
|
+
UserComment
|
|
60
|
+
FlashpixVersion 0100
|
|
61
|
+
ColorSpace Uncalibrated
|
|
62
|
+
ExifImageWidth 3008
|
|
63
|
+
ExifImageLength 2000
|
|
64
|
+
InteropIndex R98 - DCF basic file (sRGB)
|
|
65
|
+
InteropVersion 0100
|
|
66
|
+
CustomRendered Normal
|
|
67
|
+
ExposureMode Auto
|
|
68
|
+
DigitalZoomRatio 0
|
|
69
|
+
FocalLengthIn35mmFormat 450mm
|
|
70
|
+
SceneCaptureType Standard
|
|
71
|
+
GainControl None
|
|
72
|
+
Contrast Normal
|
|
73
|
+
Saturation Normal
|
|
74
|
+
Sharpness Normal
|
|
75
|
+
Compression JPEG (old-style)
|
|
76
|
+
ThumbnailOffset 40306
|
|
77
|
+
ThumbnailLength 3729
|
|
78
|
+
ProfileCMMType Win
|
|
79
|
+
ProfileVersion 2.2.0
|
|
80
|
+
ProfileClass Display Device Profile
|
|
81
|
+
ColorSpaceData RGB
|
|
82
|
+
ProfileConnectionSpace XYZ
|
|
83
|
+
ProfileDateTime 2002:05:09 20:45:47
|
|
84
|
+
ProfileFileSignature acsp
|
|
85
|
+
PrimaryPlatform Microsoft Corporation
|
|
86
|
+
CMMFlags Not Embedded, Independent
|
|
87
|
+
DeviceManufacturer KMHD
|
|
88
|
+
DeviceModel
|
|
89
|
+
DeviceAttributes Reflective, Glossy, Positive, Color
|
|
90
|
+
RenderingIntent Perceptual
|
|
91
|
+
ConnectionSpaceIlluminant 0.96419 1 0.82487
|
|
92
|
+
ProfileCreator KMHD
|
|
93
|
+
ProfileID 0
|
|
94
|
+
ProfileCopyright Copyright 2002 Minolta Co.,Ltd.
|
|
95
|
+
ProfileDescription AdobeRGB1998 ((KONICA MINOLTA CAM
|
|
96
|
+
GreenTRC (Binary data 1036 bytes, use -b option to extract)
|
|
97
|
+
RedTRC (Binary data 1036 bytes, use -b option to extract)
|
|
98
|
+
BlueTRC (Binary data 1036 bytes, use -b option to extract)
|
|
99
|
+
RedMatrixColumn 0.6097 0.3111 0.01949
|
|
100
|
+
GreenMatrixColumn 0.20529 0.62569 0.0609
|
|
101
|
+
BlueMatrixColumn 0.14919 0.06319 0.7446
|
|
102
|
+
MediaWhitePoint 0.95045 1 1.08905
|
|
103
|
+
ImageWidth 3008
|
|
104
|
+
ImageHeight 2000
|
|
105
|
+
EncodingProcess Baseline DCT, Huffman coding
|
|
106
|
+
BitsPerSample 8
|
|
107
|
+
ColorComponents 3
|
|
108
|
+
YCbCrSubSampling YCbCr4:2:2 (2 1)
|
|
109
|
+
Aperture 6.7
|
|
110
|
+
ImageSize 3008x2000
|
|
111
|
+
PreviewImage (Binary data 46166 bytes, use -b option to extract)
|
|
112
|
+
ScaleFactor35efl 1.5
|
|
113
|
+
ShutterSpeed 1/250
|
|
114
|
+
ThumbnailImage (Binary data 3729 bytes, use -b option to extract)
|
|
115
|
+
CircleOfConfusion 0.020 mm
|
|
116
|
+
FocalLength35efl 300.0mm (35mm equivalent: 450.0mm)
|
|
117
|
+
HyperfocalDistance 670.61 m
|
|
118
|
+
LightValue 13.5
|
|
119
|
+
======== /home/janfri/test_photos/2008-03-01_0004.jpg
|
|
120
|
+
ExifToolVersion 7.03
|
|
121
|
+
FileName 2008-03-01_0004.jpg
|
|
122
|
+
Directory /home/janfri/test_photos
|
|
123
|
+
FileSize 2 MB
|
|
124
|
+
FileModifyDate 2008:03:13 17:47:05
|
|
125
|
+
FileType JPEG
|
|
126
|
+
MIMEType image/jpeg
|
|
127
|
+
ExifByteOrder Big-endian (Motorola)
|
|
128
|
+
ImageDescription KONICA MINOLTA DIGITAL CAMERA
|
|
129
|
+
Make KONICA MINOLTA
|
|
130
|
+
Model DYNAX 7D
|
|
131
|
+
Orientation Horizontal (normal)
|
|
132
|
+
XResolution 72
|
|
133
|
+
YResolution 72
|
|
134
|
+
ResolutionUnit inches
|
|
135
|
+
Software DYNAX 7D v1.10
|
|
136
|
+
ModifyDate 2008:03:01 13:23:22
|
|
137
|
+
YCbCrPositioning Centered
|
|
138
|
+
ExposureTime 1/60
|
|
139
|
+
FNumber 4.5
|
|
140
|
+
ExposureProgram Program AE
|
|
141
|
+
ISO 400
|
|
142
|
+
ExifVersion 0221
|
|
143
|
+
DateTimeOriginal 2008:03:01 13:23:22
|
|
144
|
+
CreateDate 2008:03:01 13:23:22
|
|
145
|
+
ComponentsConfiguration YCbCr
|
|
146
|
+
BrightnessValue 3.5
|
|
147
|
+
ExposureCompensation 0
|
|
148
|
+
MaxApertureValue 4.5
|
|
149
|
+
MeteringMode Center-weighted average
|
|
150
|
+
LightSource Unknown (0)
|
|
151
|
+
Flash On
|
|
152
|
+
FocalLength 75.0mm
|
|
153
|
+
SubjectLocation 1504 1000 256 304
|
|
154
|
+
MakerNoteVersion MLT0
|
|
155
|
+
MinoltaImageSize Large
|
|
156
|
+
WhiteBalance Auto
|
|
157
|
+
FocusMode Single-shot AF
|
|
158
|
+
AFPoints Center
|
|
159
|
+
ISOSetting Auto
|
|
160
|
+
FreeMemoryCardImages 15
|
|
161
|
+
Rotation Rotate 270 CW
|
|
162
|
+
ImageNumber 6
|
|
163
|
+
NoiseReduction Unknown (2)
|
|
164
|
+
ImageNumber2 304
|
|
165
|
+
ZoneMatchingOn Off
|
|
166
|
+
CompressedImageSize 2191918
|
|
167
|
+
PreviewImageStart 2241539
|
|
168
|
+
PreviewImageLength 35524
|
|
169
|
+
SceneMode Standard
|
|
170
|
+
ColorMode Natural sRGB
|
|
171
|
+
MinoltaQuality Fine
|
|
172
|
+
FlashExposureComp 0
|
|
173
|
+
ImageStabilization On
|
|
174
|
+
ZoneMatching ISO Setting Used
|
|
175
|
+
ColorTemperature 0
|
|
176
|
+
LensID Tamron 18-200, 28-300 or 80-300mm F3.5-6.3
|
|
177
|
+
UserComment
|
|
178
|
+
FlashpixVersion 0100
|
|
179
|
+
ColorSpace sRGB
|
|
180
|
+
ExifImageWidth 3008
|
|
181
|
+
ExifImageLength 2000
|
|
182
|
+
InteropIndex R98 - DCF basic file (sRGB)
|
|
183
|
+
InteropVersion 0100
|
|
184
|
+
CustomRendered Normal
|
|
185
|
+
ExposureMode Auto
|
|
186
|
+
DigitalZoomRatio 0
|
|
187
|
+
FocalLengthIn35mmFormat 112mm
|
|
188
|
+
SceneCaptureType Standard
|
|
189
|
+
GainControl Low gain up
|
|
190
|
+
Contrast Normal
|
|
191
|
+
Saturation Normal
|
|
192
|
+
Sharpness Normal
|
|
193
|
+
Compression JPEG (old-style)
|
|
194
|
+
ThumbnailOffset 40306
|
|
195
|
+
ThumbnailLength 3420
|
|
196
|
+
ImageWidth 3008
|
|
197
|
+
ImageHeight 2000
|
|
198
|
+
EncodingProcess Baseline DCT, Huffman coding
|
|
199
|
+
BitsPerSample 8
|
|
200
|
+
ColorComponents 3
|
|
201
|
+
YCbCrSubSampling YCbCr4:2:2 (2 1)
|
|
202
|
+
Aperture 4.5
|
|
203
|
+
ImageSize 3008x2000
|
|
204
|
+
PreviewImage (Binary data 35524 bytes, use -b option to extract)
|
|
205
|
+
ScaleFactor35efl 1.5
|
|
206
|
+
ShutterSpeed 1/60
|
|
207
|
+
ThumbnailImage (Binary data 3420 bytes, use -b option to extract)
|
|
208
|
+
CircleOfConfusion 0.020 mm
|
|
209
|
+
FocalLength35efl 75.0mm (35mm equivalent: 112.0mm)
|
|
210
|
+
HyperfocalDistance 62.13 m
|
|
211
|
+
LightValue 8.2
|
|
212
|
+
2 image files read
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), %w(.. .. lib))
|
|
2
|
+
require 'multi_exiftool'
|
|
3
|
+
|
|
4
|
+
read_opts = [{}, {:numerical => true}, {:only => [:filename, :rotation, :orientation]}, {:numerical => true, :only => [:filename, :rotation, :orientation]}]
|
|
5
|
+
|
|
6
|
+
filenames = [['a.jpg', 'b.jpg', 'c.jpg'], ['hello world.tif'], ['a.jpg', 'b.jpg', 'c.jpg', 'hello world.tif'], ['a.jpg', 'b.jpg', 'c.jpg', 'hello world.tif', 'd.jpg']]
|
|
7
|
+
|
|
8
|
+
read_opts.each do |opts|
|
|
9
|
+
filenames.each do |fnames|
|
|
10
|
+
puts MultiExiftool::CommandGenerator.read_command(fnames, opts)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
exiftool -s -t a.jpg b.jpg c.jpg
|
|
2
|
+
exiftool -s -t "hello world.tif"
|
|
3
|
+
exiftool -s -t a.jpg b.jpg c.jpg "hello world.tif"
|
|
4
|
+
exiftool -s -t a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
5
|
+
exiftool -s -t -n a.jpg b.jpg c.jpg
|
|
6
|
+
exiftool -s -t -n "hello world.tif"
|
|
7
|
+
exiftool -s -t -n a.jpg b.jpg c.jpg "hello world.tif"
|
|
8
|
+
exiftool -s -t -n a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
9
|
+
exiftool -s -t -filename -rotation -orientation a.jpg b.jpg c.jpg
|
|
10
|
+
exiftool -s -t -filename -rotation -orientation "hello world.tif"
|
|
11
|
+
exiftool -s -t -filename -rotation -orientation a.jpg b.jpg c.jpg "hello world.tif"
|
|
12
|
+
exiftool -s -t -filename -rotation -orientation a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
13
|
+
exiftool -s -t -n -filename -rotation -orientation a.jpg b.jpg c.jpg
|
|
14
|
+
exiftool -s -t -n -filename -rotation -orientation "hello world.tif"
|
|
15
|
+
exiftool -s -t -n -filename -rotation -orientation a.jpg b.jpg c.jpg "hello world.tif"
|
|
16
|
+
exiftool -s -t -n -filename -rotation -orientation a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), %w(.. .. lib))
|
|
2
|
+
require 'multi_exiftool'
|
|
3
|
+
|
|
4
|
+
write_opts = [{}, {:numerical => true}]
|
|
5
|
+
|
|
6
|
+
changesets = [{}, {:author => 'janfri'}, {:author => 'janfri', :comment => 'some comment'}, {:author => 'janfri', :comment => 'some comment', :keywords => [:a, :b, :c]}, {:orientation => 1}]
|
|
7
|
+
|
|
8
|
+
filenames = [['a.jpg', 'b.jpg', 'c.jpg'], ['hello world.tif'], ['a.jpg', 'b.jpg', 'c.jpg', 'hello world.tif'], ['a.jpg', 'b.jpg', 'c.jpg', 'hello world.tif', 'd.jpg']]
|
|
9
|
+
|
|
10
|
+
write_opts.each do |opts|
|
|
11
|
+
changesets.each do |cs|
|
|
12
|
+
filenames.each do |fnames|
|
|
13
|
+
puts MultiExiftool::CommandGenerator.write_command(cs, fnames, opts)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
exiftool -overwrite_original a.jpg b.jpg c.jpg
|
|
2
|
+
exiftool -overwrite_original "hello world.tif"
|
|
3
|
+
exiftool -overwrite_original a.jpg b.jpg c.jpg "hello world.tif"
|
|
4
|
+
exiftool -overwrite_original a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
5
|
+
exiftool -overwrite_original -author=janfri a.jpg b.jpg c.jpg
|
|
6
|
+
exiftool -overwrite_original -author=janfri "hello world.tif"
|
|
7
|
+
exiftool -overwrite_original -author=janfri a.jpg b.jpg c.jpg "hello world.tif"
|
|
8
|
+
exiftool -overwrite_original -author=janfri a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
9
|
+
exiftool -overwrite_original -author=janfri -comment="some comment" a.jpg b.jpg c.jpg
|
|
10
|
+
exiftool -overwrite_original -author=janfri -comment="some comment" "hello world.tif"
|
|
11
|
+
exiftool -overwrite_original -author=janfri -comment="some comment" a.jpg b.jpg c.jpg "hello world.tif"
|
|
12
|
+
exiftool -overwrite_original -author=janfri -comment="some comment" a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
13
|
+
exiftool -overwrite_original -author=janfri -comment="some comment" -keywords=a -keywords=b -keywords=c a.jpg b.jpg c.jpg
|
|
14
|
+
exiftool -overwrite_original -author=janfri -comment="some comment" -keywords=a -keywords=b -keywords=c "hello world.tif"
|
|
15
|
+
exiftool -overwrite_original -author=janfri -comment="some comment" -keywords=a -keywords=b -keywords=c a.jpg b.jpg c.jpg "hello world.tif"
|
|
16
|
+
exiftool -overwrite_original -author=janfri -comment="some comment" -keywords=a -keywords=b -keywords=c a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
17
|
+
exiftool -overwrite_original -orientation=1 a.jpg b.jpg c.jpg
|
|
18
|
+
exiftool -overwrite_original -orientation=1 "hello world.tif"
|
|
19
|
+
exiftool -overwrite_original -orientation=1 a.jpg b.jpg c.jpg "hello world.tif"
|
|
20
|
+
exiftool -overwrite_original -orientation=1 a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
21
|
+
exiftool -overwrite_original -n a.jpg b.jpg c.jpg
|
|
22
|
+
exiftool -overwrite_original -n "hello world.tif"
|
|
23
|
+
exiftool -overwrite_original -n a.jpg b.jpg c.jpg "hello world.tif"
|
|
24
|
+
exiftool -overwrite_original -n a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
25
|
+
exiftool -overwrite_original -n -author=janfri a.jpg b.jpg c.jpg
|
|
26
|
+
exiftool -overwrite_original -n -author=janfri "hello world.tif"
|
|
27
|
+
exiftool -overwrite_original -n -author=janfri a.jpg b.jpg c.jpg "hello world.tif"
|
|
28
|
+
exiftool -overwrite_original -n -author=janfri a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
29
|
+
exiftool -overwrite_original -n -author=janfri -comment="some comment" a.jpg b.jpg c.jpg
|
|
30
|
+
exiftool -overwrite_original -n -author=janfri -comment="some comment" "hello world.tif"
|
|
31
|
+
exiftool -overwrite_original -n -author=janfri -comment="some comment" a.jpg b.jpg c.jpg "hello world.tif"
|
|
32
|
+
exiftool -overwrite_original -n -author=janfri -comment="some comment" a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
33
|
+
exiftool -overwrite_original -n -author=janfri -comment="some comment" -keywords=a -keywords=b -keywords=c a.jpg b.jpg c.jpg
|
|
34
|
+
exiftool -overwrite_original -n -author=janfri -comment="some comment" -keywords=a -keywords=b -keywords=c "hello world.tif"
|
|
35
|
+
exiftool -overwrite_original -n -author=janfri -comment="some comment" -keywords=a -keywords=b -keywords=c a.jpg b.jpg c.jpg "hello world.tif"
|
|
36
|
+
exiftool -overwrite_original -n -author=janfri -comment="some comment" -keywords=a -keywords=b -keywords=c a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
37
|
+
exiftool -overwrite_original -n -orientation=1 a.jpg b.jpg c.jpg
|
|
38
|
+
exiftool -overwrite_original -n -orientation=1 "hello world.tif"
|
|
39
|
+
exiftool -overwrite_original -n -orientation=1 a.jpg b.jpg c.jpg "hello world.tif"
|
|
40
|
+
exiftool -overwrite_original -n -orientation=1 a.jpg b.jpg c.jpg "hello world.tif" d.jpg
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'multi_exiftool/command_generator'
|
|
2
|
+
require 'multi_exiftool/parser'
|
|
3
|
+
require 'multi_exiftool/read_object'
|
|
4
|
+
require 'open3'
|
|
5
|
+
require 'ostruct'
|
|
6
|
+
require 'logger'
|
|
7
|
+
|
|
8
|
+
# monkey patching --- the save way :D
|
|
9
|
+
class OpenStruct
|
|
10
|
+
unless self.new.respond_to? :to_hash
|
|
11
|
+
def to_hash
|
|
12
|
+
@table
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module MultiExiftool
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
|
|
21
|
+
attr_accessor :logger
|
|
22
|
+
|
|
23
|
+
def read *filenames
|
|
24
|
+
cmd = CommandGenerator.read_command *filenames
|
|
25
|
+
stdin, stdout, stderr = Open3.popen3(cmd)
|
|
26
|
+
result = Parser.parse(stdout, stderr)
|
|
27
|
+
result.map {|r| ReadObject.new(r.data)}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def write change_set, *filenames
|
|
32
|
+
cmd = CommandGenerator.write_command change_set, *filenames
|
|
33
|
+
@logger.debug cmd
|
|
34
|
+
stdin, stdout, stderr = Open3.popen3(cmd)
|
|
35
|
+
result = Parser.parse(stdout, stderr)
|
|
36
|
+
result.inject(true) {|r| r.errors.empty?}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@logger = Logger.new nil
|
|
42
|
+
|
|
43
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module MultiExiftool
|
|
2
|
+
|
|
3
|
+
module CommandGenerator
|
|
4
|
+
|
|
5
|
+
@command = 'exiftool'
|
|
6
|
+
@std_opts_read = '-s -t'
|
|
7
|
+
@std_opts_write = '-overwrite_original'
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
|
|
11
|
+
attr_reader :std_opts_read, :std_opts_write
|
|
12
|
+
attr_accessor :command
|
|
13
|
+
|
|
14
|
+
def options_array opts={}
|
|
15
|
+
arr = []
|
|
16
|
+
arr << '-n' if opts[:numerical]
|
|
17
|
+
arr
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def write_tag_array change_set
|
|
21
|
+
arr = []
|
|
22
|
+
change_set.to_hash.each do |tag, val|
|
|
23
|
+
val_array = val.kind_of?(Array) ? val : [val]
|
|
24
|
+
val_array.each do |v|
|
|
25
|
+
arr << "-#{tag}=#{escape(v)}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
arr
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def read_tag_array tags
|
|
32
|
+
arr = []
|
|
33
|
+
tags.each do |tag|
|
|
34
|
+
arr << "-#{tag}"
|
|
35
|
+
end
|
|
36
|
+
arr
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def filenames_array filenames
|
|
40
|
+
filenames.flatten.map {|fn| escape(fn) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def read_command *args
|
|
44
|
+
files, opts = parse_args(args)
|
|
45
|
+
only = opts[:only] || []
|
|
46
|
+
[command, std_opts_read, options_array(opts), read_tag_array(only), filenames_array(files)].flatten.join(' ')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def write_command change_set, *args
|
|
50
|
+
files, opts = parse_args(args)
|
|
51
|
+
[command, std_opts_write, options_array(opts), write_tag_array(change_set), filenames_array(files)].flatten.join(' ')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def parse_args(args)
|
|
55
|
+
opts = args.last.kind_of?(Hash) ? args.pop : {}
|
|
56
|
+
[args, opts]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def escape string
|
|
60
|
+
return string unless string =~ /[ \\]/
|
|
61
|
+
'"' << string.gsub("\\") { "\\\\" } << '"'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module MultiExiftool
|
|
2
|
+
|
|
3
|
+
module Parser
|
|
4
|
+
|
|
5
|
+
# :stopdoc:
|
|
6
|
+
REGEXP_TAGLINE = /^(\w+)\t(.*)$/.freeze
|
|
7
|
+
REGEXP_STRIPLINE = /^={8} /.freeze
|
|
8
|
+
# :startdoc:
|
|
9
|
+
|
|
10
|
+
def self.parse stdout, stderr
|
|
11
|
+
result = []
|
|
12
|
+
image_info = {}
|
|
13
|
+
while !stdout.eof
|
|
14
|
+
line = stdout.readline.chomp
|
|
15
|
+
case line
|
|
16
|
+
when REGEXP_TAGLINE
|
|
17
|
+
image_info[$1] = $2
|
|
18
|
+
when REGEXP_STRIPLINE
|
|
19
|
+
unless image_info.empty?
|
|
20
|
+
result << Result.new(image_info, {})
|
|
21
|
+
end
|
|
22
|
+
image_info = {}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
unless image_info.empty?
|
|
26
|
+
result << Result.new(image_info, {})
|
|
27
|
+
end
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Result
|
|
32
|
+
|
|
33
|
+
attr_reader :data, :errors
|
|
34
|
+
|
|
35
|
+
def initialize data, errors
|
|
36
|
+
@data, @errors = data.freeze, errors.freeze
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module MultiExiftool
|
|
2
|
+
|
|
3
|
+
class ReadObject
|
|
4
|
+
|
|
5
|
+
def initialize values={}
|
|
6
|
+
@values = {}
|
|
7
|
+
values.each do |tag, val|
|
|
8
|
+
@values[unify(tag)] = val
|
|
9
|
+
end
|
|
10
|
+
@values.freeze
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def each
|
|
14
|
+
@values.each do |tag, val|
|
|
15
|
+
yield tag, convert(val)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def [] tag
|
|
20
|
+
convert(@values[unify(tag)])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
attr_accessor :converting_rules
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def unify tag
|
|
30
|
+
tag.gsub(/[\-_]/, '').downcase
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def convert val
|
|
34
|
+
result = val
|
|
35
|
+
ReadObject.converting_rules.each do |test, proc|
|
|
36
|
+
if test === val
|
|
37
|
+
result = proc.call(val)
|
|
38
|
+
break
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def method_missing sym, *args
|
|
45
|
+
name = unify(sym.id2name)
|
|
46
|
+
self[name]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@converting_rules = []
|
|
50
|
+
|
|
51
|
+
@converting_rules << [/^0+\d+$/, lambda {|val| val}]
|
|
52
|
+
@converting_rules << [/^[+\-]?\d+$/, lambda {|val| val.to_i}]
|
|
53
|
+
@converting_rules << [/^[+\-]?\d+\.\d+$/, lambda {|val| val.to_f}]
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = %q{multi_exiftool}
|
|
5
|
+
s.version = "0.0.1"
|
|
6
|
+
|
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
|
8
|
+
s.authors = ["Jan Friedrich"]
|
|
9
|
+
s.date = %q{2008-11-25}
|
|
10
|
+
s.description = %q{}
|
|
11
|
+
s.email = %q{janfri.rubyforge@gmail.com}
|
|
12
|
+
s.extra_rdoc_files = ["lib/multi_exiftool.rb", "lib/multi_exiftool/parser.rb", "lib/multi_exiftool/command_generator.rb", "lib/multi_exiftool/read_object.rb", "README", "CHANGELOG", "COPYING"]
|
|
13
|
+
s.files = ["Rakefile", "Manifest", "test/test_command_generator.rb", "test/test_helper.rb", "test/test_parser.rb", "test/test_read_object.rb", "lib/multi_exiftool.rb", "lib/multi_exiftool/parser.rb", "lib/multi_exiftool/command_generator.rb", "lib/multi_exiftool/read_object.rb", "README", "data/fixtures/read_non_existing_file.stderr", "data/fixtures/read_non_existing_file.stdout", "data/fixtures/read_two_files.stderr", "data/fixtures/read_one_file.stderr", "data/fixtures/read_one_file.stdout", "data/fixtures/read_two_files.stdout", "data/regression/read_command.rb", "data/regression/read_command.rb.out", "data/regression/write_command.rb", "data/regression/write_command.rb.out", "script/generate_fixture.rb", "script/colorize.rb", "CHANGELOG", "COPYING", "multi_exiftool.gemspec"]
|
|
14
|
+
s.has_rdoc = true
|
|
15
|
+
s.homepage = %q{http://multiexiftool.rubyforge.org}
|
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Multi_exiftool", "--main", "README"]
|
|
17
|
+
s.require_paths = ["lib"]
|
|
18
|
+
s.rubyforge_project = %q{multiexiftool}
|
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
|
20
|
+
s.summary = %q{}
|
|
21
|
+
s.test_files = ["test/test_read_object.rb", "test/test_helper.rb", "test/test_command_generator.rb", "test/test_parser.rb"]
|
|
22
|
+
|
|
23
|
+
if s.respond_to? :specification_version then
|
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
25
|
+
s.specification_version = 2
|
|
26
|
+
|
|
27
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
|
28
|
+
s.add_development_dependency(%q<yact>, [">= 0"])
|
|
29
|
+
else
|
|
30
|
+
s.add_dependency(%q<yact>, [">= 0"])
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
s.add_dependency(%q<yact>, [">= 0"])
|
|
34
|
+
end
|
|
35
|
+
end
|
data/script/colorize.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
if ARGV.size < 2
|
|
4
|
+
$stderr.puts "usage: #$0 [fixturename] [exiftool-opts]* [filename]+"
|
|
5
|
+
exit(-1)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
fixture = ARGV.shift
|
|
9
|
+
params = ARGV.map {|a| a.sub(/(.+?=)(.*)/, '\1"\2"')}.join(' ')
|
|
10
|
+
|
|
11
|
+
fixture_filename = File.join(File.dirname(__FILE__), %w(.. data fixtures), fixture)
|
|
12
|
+
|
|
13
|
+
cmd = "exiftool -s -t #{params} > #{fixture_filename}.stdout 2> #{fixture_filename}.stderr"
|
|
14
|
+
|
|
15
|
+
if $DEBUG
|
|
16
|
+
puts cmd
|
|
17
|
+
exit 0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
result = system cmd
|
|
21
|
+
|
|
22
|
+
if File.exist?("#{fixture_filename}.stdout") && File.exist?("#{fixture_filename}.stderr")
|
|
23
|
+
puts "Fixture #{fixture} generated."
|
|
24
|
+
else
|
|
25
|
+
puts "Error while generating fixture #{fixture}."
|
|
26
|
+
exit(-1)
|
|
27
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
context 'CommandGenerator' do
|
|
4
|
+
|
|
5
|
+
context 'extra options' do
|
|
6
|
+
|
|
7
|
+
test 'numerical' do
|
|
8
|
+
assert_equal [], CommandGenerator.options_array
|
|
9
|
+
assert_equal ['-n'], CommandGenerator.options_array(:numerical => true)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'write_tag_array' do
|
|
15
|
+
|
|
16
|
+
setup do
|
|
17
|
+
@change_set = OpenStruct.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
test 'is empty for empty change set' do
|
|
21
|
+
assert_equal [], CommandGenerator.write_tag_array(@change_set)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
test 'is correct for change set with some values' do
|
|
25
|
+
author = 'janfri'
|
|
26
|
+
comment = 'some_comment'
|
|
27
|
+
@change_set.author = author
|
|
28
|
+
@change_set.comment = comment
|
|
29
|
+
assert_equal ["-author=#{author}", "-comment=#{comment}"], CommandGenerator.write_tag_array(@change_set)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test 'works with array values aka list tags' do
|
|
33
|
+
keywords = %w(red yellow green)
|
|
34
|
+
@change_set.keywords = keywords
|
|
35
|
+
assert_equal ["-keywords=red", "-keywords=yellow", "-keywords=green"], CommandGenerator.write_tag_array(@change_set)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'filenames_array' do
|
|
41
|
+
|
|
42
|
+
test 'simple names' do
|
|
43
|
+
filenames = %w(a.jpg b.jpg c.tif)
|
|
44
|
+
assert_equal filenames, CommandGenerator.filenames_array(filenames)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
test 'names with spaces' do
|
|
48
|
+
filenames = ['hello world.jpg', 'how are you.tif']
|
|
49
|
+
assert_equal filenames.map {|fn| '"' << fn << '"'}, CommandGenerator.filenames_array(filenames)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'read_tag_array' do
|
|
55
|
+
|
|
56
|
+
test 'is empty for empty tag array' do
|
|
57
|
+
assert_equal [], CommandGenerator.read_tag_array([])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
test 'is correct for some tags' do
|
|
61
|
+
tags = [:filename, :rotation, :orientation]
|
|
62
|
+
assert_equal %w(-filename -rotation -orientation), CommandGenerator.read_tag_array(tags)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
context 'escape' do
|
|
68
|
+
|
|
69
|
+
test 'strings without spaces and backspace are not changed' do
|
|
70
|
+
simple = 'simple'
|
|
71
|
+
assert_equal simple, CommandGenerator.escape(simple)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
test 'strings with spaces and no backslash are simple enclosed in quotes' do
|
|
75
|
+
string = 'string with spaces'
|
|
76
|
+
assert_equal string.inspect, CommandGenerator.escape(string)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
test 'strings with backslashes are enclosed by quotes and backspaces are masked' do
|
|
80
|
+
string = 'backslash\string'
|
|
81
|
+
assert_equal string.inspect, CommandGenerator.escape(string)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# TODO: read_command, write_command
|
|
88
|
+
|
|
89
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
end
|
|
5
|
+
require 'ostruct'
|
|
6
|
+
require 'yact'
|
|
7
|
+
require 'multi_exiftool'
|
|
8
|
+
|
|
9
|
+
class Fixture
|
|
10
|
+
|
|
11
|
+
DIR = File.join(File.dirname(__FILE__), %w(.. data fixtures))
|
|
12
|
+
|
|
13
|
+
def initialize name
|
|
14
|
+
@name = name
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stdout
|
|
18
|
+
StringIO.new(File.read(File.join(DIR, @name + '.stdout')))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def stderr
|
|
22
|
+
StringIO.new(File.read(File.join(DIR, @name + '.stderr')))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Class instance variable!
|
|
26
|
+
@hash = {}
|
|
27
|
+
|
|
28
|
+
Dir[File.join(Fixture::DIR, '*.stdout')].each do |filename|
|
|
29
|
+
fixture_name = File.basename(filename, '.stdout')
|
|
30
|
+
@hash[fixture_name] = Fixture.new(fixture_name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
|
|
35
|
+
def method_missing sym, *args
|
|
36
|
+
return @hash[sym.to_s] unless @hash[sym.to_s].nil?
|
|
37
|
+
raise "Fixture #{sym} does not exist!"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
include MultiExiftool
|
data/test/test_parser.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
context 'Parser.parse' do
|
|
4
|
+
|
|
5
|
+
context 'read_non_existing_file' do
|
|
6
|
+
|
|
7
|
+
test 'returns an empty array' do
|
|
8
|
+
fix = Fixture.read_non_existing_file
|
|
9
|
+
assert_equal [], Parser.parse(fix.stdout, fix.stderr)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'read_one_file' do
|
|
15
|
+
|
|
16
|
+
setup do
|
|
17
|
+
fix = Fixture.read_one_file
|
|
18
|
+
@result = Parser.parse(fix.stdout, fix.stderr)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test 'returns an array with one result of meaningful data' do
|
|
22
|
+
assert_kind_of Array, @result
|
|
23
|
+
assert_equal 1, @result.size
|
|
24
|
+
data = @result.first.data
|
|
25
|
+
assert_equal '2005:10:06 15:41:08', data['DateTimeOriginal']
|
|
26
|
+
assert_equal '9.0', data['FNumber']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context 'read_two_files' do
|
|
32
|
+
|
|
33
|
+
setup do
|
|
34
|
+
fix = Fixture.read_two_files
|
|
35
|
+
@result = Parser.parse(fix.stdout, fix.stderr)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
test 'returns an array with two results of meaningful data' do
|
|
39
|
+
assert_kind_of Array, @result
|
|
40
|
+
assert_equal 2, @result.size
|
|
41
|
+
data1 = @result[0].data
|
|
42
|
+
assert_equal '2008:02:16 16:10:52', data1['DateTimeOriginal']
|
|
43
|
+
assert_equal '6.7', data1['FNumber']
|
|
44
|
+
data2 = @result[1].data
|
|
45
|
+
assert_equal '2008:03:01 13:23:22', data2['DateTimeOriginal']
|
|
46
|
+
assert_equal '4.5', data2['FNumber']
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
context 'ReadObject' do
|
|
4
|
+
|
|
5
|
+
setup do
|
|
6
|
+
@values = { 'Author' => 'janfri', 'Comment' => 'Test' }
|
|
7
|
+
@read_object = ReadObject.new @values
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
test 'Value access via []' do
|
|
11
|
+
@values.each do |key, val|
|
|
12
|
+
assert_equal val, @read_object[key], "Test: @read_object['#{key}']"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
test 'Value access via method call' do
|
|
17
|
+
assert_equal @read_object['Author'], @read_object.author
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'Semantical values' do
|
|
21
|
+
|
|
22
|
+
setup do
|
|
23
|
+
@values = {'String' => 'some string', 'Integer' => '123', 'Float' => '1.23'}
|
|
24
|
+
@read_object = ReadObject.new @values
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
[String, Integer, Float].each do |klass|
|
|
28
|
+
test klass.to_s do
|
|
29
|
+
assert_kind_of klass, @read_object[klass.to_s]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: multi_exiftool
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jan Friedrich
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2008-11-25 00:00:00 +01:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: yact
|
|
17
|
+
type: :development
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: "0"
|
|
24
|
+
version:
|
|
25
|
+
description: ""
|
|
26
|
+
email: janfri.rubyforge@gmail.com
|
|
27
|
+
executables: []
|
|
28
|
+
|
|
29
|
+
extensions: []
|
|
30
|
+
|
|
31
|
+
extra_rdoc_files:
|
|
32
|
+
- lib/multi_exiftool.rb
|
|
33
|
+
- lib/multi_exiftool/parser.rb
|
|
34
|
+
- lib/multi_exiftool/command_generator.rb
|
|
35
|
+
- lib/multi_exiftool/read_object.rb
|
|
36
|
+
- README
|
|
37
|
+
- CHANGELOG
|
|
38
|
+
- COPYING
|
|
39
|
+
files:
|
|
40
|
+
- Rakefile
|
|
41
|
+
- Manifest
|
|
42
|
+
- test/test_command_generator.rb
|
|
43
|
+
- test/test_helper.rb
|
|
44
|
+
- test/test_parser.rb
|
|
45
|
+
- test/test_read_object.rb
|
|
46
|
+
- lib/multi_exiftool.rb
|
|
47
|
+
- lib/multi_exiftool/parser.rb
|
|
48
|
+
- lib/multi_exiftool/command_generator.rb
|
|
49
|
+
- lib/multi_exiftool/read_object.rb
|
|
50
|
+
- README
|
|
51
|
+
- data/fixtures/read_non_existing_file.stderr
|
|
52
|
+
- data/fixtures/read_non_existing_file.stdout
|
|
53
|
+
- data/fixtures/read_two_files.stderr
|
|
54
|
+
- data/fixtures/read_one_file.stderr
|
|
55
|
+
- data/fixtures/read_one_file.stdout
|
|
56
|
+
- data/fixtures/read_two_files.stdout
|
|
57
|
+
- data/regression/read_command.rb
|
|
58
|
+
- data/regression/read_command.rb.out
|
|
59
|
+
- data/regression/write_command.rb
|
|
60
|
+
- data/regression/write_command.rb.out
|
|
61
|
+
- script/generate_fixture.rb
|
|
62
|
+
- script/colorize.rb
|
|
63
|
+
- CHANGELOG
|
|
64
|
+
- COPYING
|
|
65
|
+
- multi_exiftool.gemspec
|
|
66
|
+
has_rdoc: true
|
|
67
|
+
homepage: http://multiexiftool.rubyforge.org
|
|
68
|
+
post_install_message:
|
|
69
|
+
rdoc_options:
|
|
70
|
+
- --line-numbers
|
|
71
|
+
- --inline-source
|
|
72
|
+
- --title
|
|
73
|
+
- Multi_exiftool
|
|
74
|
+
- --main
|
|
75
|
+
- README
|
|
76
|
+
require_paths:
|
|
77
|
+
- lib
|
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: "0"
|
|
83
|
+
version:
|
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: "1.2"
|
|
89
|
+
version:
|
|
90
|
+
requirements: []
|
|
91
|
+
|
|
92
|
+
rubyforge_project: multiexiftool
|
|
93
|
+
rubygems_version: 1.3.1
|
|
94
|
+
signing_key:
|
|
95
|
+
specification_version: 2
|
|
96
|
+
summary: ""
|
|
97
|
+
test_files:
|
|
98
|
+
- test/test_read_object.rb
|
|
99
|
+
- test/test_helper.rb
|
|
100
|
+
- test/test_command_generator.rb
|
|
101
|
+
- test/test_parser.rb
|