multi_exiftool 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|