riiif 2.1.0 → 2.8.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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ruby.yml +39 -0
- data/.rubocop_todo.yml +115 -16
- data/Gemfile +2 -12
- data/README.md +29 -9
- data/app/controllers/riiif/images_controller.rb +1 -1
- data/app/extractors/riiif/abstract_info_extractor.rb +12 -0
- data/app/{services → extractors}/riiif/image_magick_info_extractor.rb +2 -7
- data/app/extractors/riiif/vips_info_extractor.rb +22 -0
- data/app/models/riiif/file.rb +11 -24
- data/app/models/riiif/image.rb +0 -3
- data/app/resolvers/riiif/abstract_file_system_resolver.rb +1 -11
- data/app/resolvers/riiif/akubra_system_file_resolver.rb +1 -0
- data/app/resolvers/riiif/file_system_file_resolver.rb +1 -1
- data/app/resolvers/riiif/http_file_resolver.rb +11 -3
- data/app/services/riiif/crop.rb +40 -0
- data/app/services/riiif/imagemagick_command_factory.rb +1 -1
- data/app/services/riiif/vips_resize.rb +47 -0
- data/app/transformers/riiif/vips_transformer.rb +103 -0
- data/docs/vips_comparison.md +257 -0
- data/lib/riiif/engine.rb +14 -0
- data/lib/riiif/version.rb +1 -1
- data/lib/riiif.rb +14 -0
- data/riiif.gemspec +9 -7
- data/spec/controllers/riiif/images_controller_spec.rb +8 -8
- data/spec/extractors/riiif/image_magick_info_extractor_spec.rb +31 -0
- data/spec/extractors/riiif/vips_info_extractor_spec.rb +76 -0
- data/spec/fixtures/test.jpg +0 -0
- data/spec/fixtures/test.png +0 -0
- data/spec/fixtures/test.tif +0 -0
- data/spec/models/riiif/file_spec.rb +35 -0
- data/spec/models/riiif/file_system_file_resolver_spec.rb +7 -0
- data/spec/models/riiif/http_file_resolver_spec.rb +1 -1
- data/spec/models/riiif/image_spec.rb +27 -21
- data/spec/spec_helper.rb +0 -6
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +6 -1
- data/spec/transformers/riiif/kakadu_transformer_spec.rb +8 -8
- data/spec/transformers/riiif/vips_transformer_spec.rb +271 -0
- metadata +49 -19
- data/.travis.yml +0 -11
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
RSpec.describe Riiif::File do
|
|
2
|
+
describe '#info_extractor_class' do
|
|
3
|
+
subject { described_class.info_extractor_class }
|
|
4
|
+
|
|
5
|
+
context 'when not using vips' do
|
|
6
|
+
it { is_expected.to eq Riiif::ImageMagickInfoExtractor }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
context 'when vips is configured' do
|
|
10
|
+
before { allow(Riiif).to receive(:use_vips?).and_return true }
|
|
11
|
+
|
|
12
|
+
it { is_expected.to eq Riiif::VipsInfoExtractor }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe '#transformer' do
|
|
17
|
+
subject { described_class.new('path.jp2', double).transformer }
|
|
18
|
+
|
|
19
|
+
context 'when vips is configured' do
|
|
20
|
+
before { allow(Riiif).to receive(:use_vips?).and_return true }
|
|
21
|
+
|
|
22
|
+
it { is_expected.to eq Riiif::VipsTransformer }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'when Kakadu is enabled' do
|
|
26
|
+
before { allow(Riiif).to receive(:kakadu_enabled?).and_return true }
|
|
27
|
+
|
|
28
|
+
it { is_expected.to eq Riiif::KakaduTransformer }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context 'when using image/graphicsmagick without Kakadu' do
|
|
32
|
+
it { is_expected.to eq Riiif::ImagemagickTransformer }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -78,5 +78,12 @@ describe Riiif::FileSystemFileResolver do
|
|
|
78
78
|
expect(subject).to be_nil
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
|
+
|
|
82
|
+
context 'as an integer' do
|
|
83
|
+
let(:id) { 1 }
|
|
84
|
+
it 'converts it to a string before parsing' do
|
|
85
|
+
expect { subject }.not_to raise_error
|
|
86
|
+
end
|
|
87
|
+
end
|
|
81
88
|
end
|
|
82
89
|
end
|
|
@@ -20,7 +20,7 @@ RSpec.describe Riiif::Image do
|
|
|
20
20
|
|
|
21
21
|
it 'renders' do
|
|
22
22
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
23
|
-
.with("convert -quality 85 -sampling-factor 4:2:0 -strip #{filename} jpg:-")
|
|
23
|
+
.with("convert -quality 85 -sampling-factor 4:2:0 -strip \'#{filename}\' jpg:-")
|
|
24
24
|
.and_return('imagedata')
|
|
25
25
|
|
|
26
26
|
expect(subject.render('size' => 'full', format: 'jpg')).to eq 'imagedata'
|
|
@@ -28,6 +28,9 @@ RSpec.describe Riiif::Image do
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
it 'is able to override the file used for the Image' do
|
|
31
|
+
allow(Riiif::CommandRunner).to receive(:execute)
|
|
32
|
+
.with("identify -format '%h %w %m %[channels]' \'#{filename}[0]\'").and_return('400 800')
|
|
33
|
+
|
|
31
34
|
img = described_class.new('some_id', Riiif::File.new(filename))
|
|
32
35
|
expect(img.id).to eq 'some_id'
|
|
33
36
|
expect(img.info).to eq Riiif::ImageInformation.new(width: 800, height: 400)
|
|
@@ -35,13 +38,16 @@ RSpec.describe Riiif::Image do
|
|
|
35
38
|
|
|
36
39
|
describe 'info' do
|
|
37
40
|
it 'returns the data' do
|
|
41
|
+
allow(Riiif::CommandRunner).to receive(:execute)
|
|
42
|
+
.with("identify -format '%h %w %m %[channels]' \'#{filename}[0]\'").and_return('400 800')
|
|
43
|
+
|
|
38
44
|
expect(subject.info).to eq Riiif::ImageInformation.new(width: 800, height: 400)
|
|
39
45
|
end
|
|
40
46
|
end
|
|
41
47
|
|
|
42
|
-
context 'using
|
|
48
|
+
context 'using HttpFileResolver' do
|
|
43
49
|
before do
|
|
44
|
-
described_class.file_resolver = Riiif::
|
|
50
|
+
described_class.file_resolver = Riiif::HttpFileResolver.new
|
|
45
51
|
described_class.file_resolver.id_to_uri = lambda do |id|
|
|
46
52
|
"https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/#{id}.jpg/600px-#{id}.jpg"
|
|
47
53
|
end
|
|
@@ -71,7 +77,7 @@ RSpec.describe Riiif::Image do
|
|
|
71
77
|
describe '#render' do
|
|
72
78
|
before do
|
|
73
79
|
allow(Riiif::CommandRunner).to receive(:execute)
|
|
74
|
-
.with("identify -format '%h %w %m %[channels]' #{filename}[0]").and_return('131 175 JPEG')
|
|
80
|
+
.with("identify -format '%h %w %m %[channels]' \'#{filename}[0]\'").and_return('131 175 JPEG')
|
|
75
81
|
end
|
|
76
82
|
|
|
77
83
|
describe 'region' do
|
|
@@ -82,7 +88,7 @@ RSpec.describe Riiif::Image do
|
|
|
82
88
|
|
|
83
89
|
it 'returns the original' do
|
|
84
90
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
85
|
-
.with("convert -strip #{filename} png:-")
|
|
91
|
+
.with("convert -strip \'#{filename}\' png:-")
|
|
86
92
|
render
|
|
87
93
|
end
|
|
88
94
|
end
|
|
@@ -92,7 +98,7 @@ RSpec.describe Riiif::Image do
|
|
|
92
98
|
|
|
93
99
|
it 'runs the correct imagemagick command' do
|
|
94
100
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
95
|
-
.with("convert -crop 60x75+80+15 -strip #{filename} png:-")
|
|
101
|
+
.with("convert -crop 60x75+80+15 -strip \'#{filename}\' png:-")
|
|
96
102
|
render
|
|
97
103
|
end
|
|
98
104
|
end
|
|
@@ -102,7 +108,7 @@ RSpec.describe Riiif::Image do
|
|
|
102
108
|
|
|
103
109
|
it 'runs the correct imagemagick command' do
|
|
104
110
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
105
|
-
.with("convert -crop 80.0%x70.0+18+13 -strip #{filename} png:-")
|
|
111
|
+
.with("convert -crop 80.0%x70.0+18+13 -strip \'#{filename}\' png:-")
|
|
106
112
|
render
|
|
107
113
|
end
|
|
108
114
|
end
|
|
@@ -112,7 +118,7 @@ RSpec.describe Riiif::Image do
|
|
|
112
118
|
|
|
113
119
|
it 'runs the correct imagemagick command' do
|
|
114
120
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
115
|
-
.with("convert -crop 131x131+22+0 -strip #{filename} png:-")
|
|
121
|
+
.with("convert -crop 131x131+22+0 -strip \'#{filename}\' png:-")
|
|
116
122
|
render
|
|
117
123
|
end
|
|
118
124
|
end
|
|
@@ -134,7 +140,7 @@ RSpec.describe Riiif::Image do
|
|
|
134
140
|
|
|
135
141
|
it 'returns the original' do
|
|
136
142
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
137
|
-
.with("convert -strip #{filename} png:-")
|
|
143
|
+
.with("convert -strip \'#{filename}\' png:-")
|
|
138
144
|
render
|
|
139
145
|
end
|
|
140
146
|
end
|
|
@@ -144,7 +150,7 @@ RSpec.describe Riiif::Image do
|
|
|
144
150
|
|
|
145
151
|
it 'runs the correct imagemagick command' do
|
|
146
152
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
147
|
-
.with("convert -resize 50.0% -strip #{filename} png:-")
|
|
153
|
+
.with("convert -resize 50.0% -strip \'#{filename}\' png:-")
|
|
148
154
|
render
|
|
149
155
|
end
|
|
150
156
|
end
|
|
@@ -154,7 +160,7 @@ RSpec.describe Riiif::Image do
|
|
|
154
160
|
|
|
155
161
|
it 'runs the correct imagemagick command' do
|
|
156
162
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
157
|
-
.with("convert -resize 12.5% -strip #{filename} png:-")
|
|
163
|
+
.with("convert -resize 12.5% -strip \'#{filename}\' png:-")
|
|
158
164
|
render
|
|
159
165
|
end
|
|
160
166
|
end
|
|
@@ -164,7 +170,7 @@ RSpec.describe Riiif::Image do
|
|
|
164
170
|
|
|
165
171
|
it 'runs the correct imagemagick command' do
|
|
166
172
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
167
|
-
.with("convert -resize 50 -strip #{filename} png:-")
|
|
173
|
+
.with("convert -resize 50 -strip \'#{filename}\' png:-")
|
|
168
174
|
render
|
|
169
175
|
end
|
|
170
176
|
end
|
|
@@ -174,7 +180,7 @@ RSpec.describe Riiif::Image do
|
|
|
174
180
|
|
|
175
181
|
it 'runs the correct imagemagick command' do
|
|
176
182
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
177
|
-
.with("convert -resize x50 -strip #{filename} png:-")
|
|
183
|
+
.with("convert -resize x50 -strip \'#{filename}\' png:-")
|
|
178
184
|
render
|
|
179
185
|
end
|
|
180
186
|
end
|
|
@@ -184,7 +190,7 @@ RSpec.describe Riiif::Image do
|
|
|
184
190
|
|
|
185
191
|
it 'runs the correct imagemagick command' do
|
|
186
192
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
187
|
-
.with("convert -resize 150x75! -strip #{filename} png:-")
|
|
193
|
+
.with("convert -resize 150x75! -strip \'#{filename}\' png:-")
|
|
188
194
|
render
|
|
189
195
|
end
|
|
190
196
|
end
|
|
@@ -193,7 +199,7 @@ RSpec.describe Riiif::Image do
|
|
|
193
199
|
|
|
194
200
|
it 'runs the correct imagemagick command' do
|
|
195
201
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
196
|
-
.with("convert -resize 150x75 -strip #{filename} png:-")
|
|
202
|
+
.with("convert -resize 150x75 -strip \'#{filename}\' png:-")
|
|
197
203
|
render
|
|
198
204
|
end
|
|
199
205
|
end
|
|
@@ -215,7 +221,7 @@ RSpec.describe Riiif::Image do
|
|
|
215
221
|
|
|
216
222
|
it 'returns the original' do
|
|
217
223
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
218
|
-
.with("convert -strip #{filename} png:-")
|
|
224
|
+
.with("convert -strip \'#{filename}\' png:-")
|
|
219
225
|
render
|
|
220
226
|
end
|
|
221
227
|
end
|
|
@@ -225,7 +231,7 @@ RSpec.describe Riiif::Image do
|
|
|
225
231
|
|
|
226
232
|
it 'handles floats' do
|
|
227
233
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
228
|
-
.with("convert -virtual-pixel white +distort srt 22.5 -strip #{filename} png:-")
|
|
234
|
+
.with("convert -virtual-pixel white +distort srt 22.5 -strip \'#{filename}\' png:-")
|
|
229
235
|
render
|
|
230
236
|
end
|
|
231
237
|
end
|
|
@@ -247,7 +253,7 @@ RSpec.describe Riiif::Image do
|
|
|
247
253
|
|
|
248
254
|
it 'returns the original when specifing default' do
|
|
249
255
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
250
|
-
.with("convert -strip #{filename} png:-")
|
|
256
|
+
.with("convert -strip \'#{filename}\' png:-")
|
|
251
257
|
render
|
|
252
258
|
end
|
|
253
259
|
end
|
|
@@ -257,7 +263,7 @@ RSpec.describe Riiif::Image do
|
|
|
257
263
|
|
|
258
264
|
it 'returns the original when specifing color' do
|
|
259
265
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
260
|
-
.with("convert -strip #{filename} png:-")
|
|
266
|
+
.with("convert -strip \'#{filename}\' png:-")
|
|
261
267
|
render
|
|
262
268
|
end
|
|
263
269
|
end
|
|
@@ -267,7 +273,7 @@ RSpec.describe Riiif::Image do
|
|
|
267
273
|
|
|
268
274
|
it 'converts to grayscale' do
|
|
269
275
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
270
|
-
.with("convert -colorspace Gray -strip #{filename} png:-")
|
|
276
|
+
.with("convert -colorspace Gray -strip \'#{filename}\' png:-")
|
|
271
277
|
render
|
|
272
278
|
end
|
|
273
279
|
end
|
|
@@ -277,7 +283,7 @@ RSpec.describe Riiif::Image do
|
|
|
277
283
|
|
|
278
284
|
it 'converts to bitonal' do
|
|
279
285
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
280
|
-
.with("convert -colorspace Gray -type Bilevel -strip #{filename} png:-")
|
|
286
|
+
.with("convert -colorspace Gray -type Bilevel -strip \'#{filename}\' png:-")
|
|
281
287
|
render
|
|
282
288
|
end
|
|
283
289
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
require 'rails/generators'
|
|
2
2
|
|
|
3
3
|
class TestAppGenerator < Rails::Generators::Base
|
|
4
|
-
source_root 'spec/test_app_templates'
|
|
4
|
+
# source_root 'spec/test_app_templates'
|
|
5
|
+
source_root File.expand_path("../../../spec", __dir__)
|
|
5
6
|
|
|
6
7
|
def add_routes
|
|
7
8
|
route "mount Riiif::Engine => '/images', as: 'riiif'"
|
|
8
9
|
end
|
|
10
|
+
|
|
11
|
+
def copy_fixtures
|
|
12
|
+
directory 'fixtures', 'spec/fixtures'
|
|
13
|
+
end
|
|
9
14
|
end
|
|
@@ -42,7 +42,7 @@ RSpec.describe Riiif::KakaduTransformer do
|
|
|
42
42
|
.with('kdu_expand -quiet -i baseball.jp2 -num_threads 4 ' \
|
|
43
43
|
'-region "{0.1,0.2},{0.1,0.1}" -reduce 4 -o /tmp/foo.bmp')
|
|
44
44
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
45
|
-
.with('convert -resize 38x38! -quality 85 -sampling-factor 4:2:0 -strip /tmp/foo.bmp jpg:-')
|
|
45
|
+
.with('convert -resize 38x38! -quality 85 -sampling-factor 4:2:0 -strip \'/tmp/foo.bmp\' jpg:-')
|
|
46
46
|
transform
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -54,7 +54,7 @@ RSpec.describe Riiif::KakaduTransformer do
|
|
|
54
54
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
55
55
|
.with('kdu_expand -quiet -i baseball.jp2 -num_threads 4 -o /tmp/foo.bmp')
|
|
56
56
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
57
|
-
.with('convert -quality 85 -sampling-factor 4:2:0 -strip /tmp/foo.bmp jpg:-')
|
|
57
|
+
.with('convert -quality 85 -sampling-factor 4:2:0 -strip \'/tmp/foo.bmp\' jpg:-')
|
|
58
58
|
transform
|
|
59
59
|
end
|
|
60
60
|
end
|
|
@@ -67,7 +67,7 @@ RSpec.describe Riiif::KakaduTransformer do
|
|
|
67
67
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
68
68
|
.with('kdu_expand -quiet -i baseball.jp2 -num_threads 4 -o /tmp/foo.bmp')
|
|
69
69
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
70
|
-
.with('convert -resize 651 -quality 85 -sampling-factor 4:2:0 -strip /tmp/foo.bmp jpg:-')
|
|
70
|
+
.with('convert -resize 651 -quality 85 -sampling-factor 4:2:0 -strip \'/tmp/foo.bmp\' jpg:-')
|
|
71
71
|
transform
|
|
72
72
|
end
|
|
73
73
|
end
|
|
@@ -80,7 +80,7 @@ RSpec.describe Riiif::KakaduTransformer do
|
|
|
80
80
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
81
81
|
.with('kdu_expand -quiet -i baseball.jp2 -num_threads 4 -o /tmp/foo.bmp')
|
|
82
82
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
83
|
-
.with('convert -resize x581 -quality 85 -sampling-factor 4:2:0 -strip /tmp/foo.bmp jpg:-')
|
|
83
|
+
.with('convert -resize x581 -quality 85 -sampling-factor 4:2:0 -strip \'/tmp/foo.bmp\' jpg:-')
|
|
84
84
|
transform
|
|
85
85
|
end
|
|
86
86
|
end
|
|
@@ -96,7 +96,7 @@ RSpec.describe Riiif::KakaduTransformer do
|
|
|
96
96
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
97
97
|
.with('kdu_expand -quiet -i baseball.jp2 -num_threads 4 -reduce 1 -o /tmp/foo.bmp')
|
|
98
98
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
99
|
-
.with('convert -resize 60.0% -quality 85 -sampling-factor 4:2:0 -strip /tmp/foo.bmp jpg:-')
|
|
99
|
+
.with('convert -resize 60.0% -quality 85 -sampling-factor 4:2:0 -strip \'/tmp/foo.bmp\' jpg:-')
|
|
100
100
|
transform
|
|
101
101
|
end
|
|
102
102
|
end
|
|
@@ -109,7 +109,7 @@ RSpec.describe Riiif::KakaduTransformer do
|
|
|
109
109
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
110
110
|
.with('kdu_expand -quiet -i baseball.jp2 -num_threads 4 -reduce 1 -o /tmp/foo.bmp')
|
|
111
111
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
112
|
-
.with('convert -resize 408 -quality 85 -sampling-factor 4:2:0 -strip /tmp/foo.bmp jpg:-')
|
|
112
|
+
.with('convert -resize 408 -quality 85 -sampling-factor 4:2:0 -strip \'/tmp/foo.bmp\' jpg:-')
|
|
113
113
|
transform
|
|
114
114
|
end
|
|
115
115
|
end
|
|
@@ -122,7 +122,7 @@ RSpec.describe Riiif::KakaduTransformer do
|
|
|
122
122
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
123
123
|
.with('kdu_expand -quiet -i baseball.jp2 -num_threads 4 -reduce 1 -o /tmp/foo.bmp')
|
|
124
124
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
125
|
-
.with('convert -resize x481 -quality 85 -sampling-factor 4:2:0 -strip /tmp/foo.bmp jpg:-')
|
|
125
|
+
.with('convert -resize x481 -quality 85 -sampling-factor 4:2:0 -strip \'/tmp/foo.bmp\' jpg:-')
|
|
126
126
|
transform
|
|
127
127
|
end
|
|
128
128
|
end
|
|
@@ -135,7 +135,7 @@ RSpec.describe Riiif::KakaduTransformer do
|
|
|
135
135
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
136
136
|
.with('kdu_expand -quiet -i baseball.jp2 -num_threads 4 -reduce 2 -o /tmp/foo.bmp')
|
|
137
137
|
expect(Riiif::CommandRunner).to receive(:execute)
|
|
138
|
-
.with('convert -resize 80.0% -quality 85 -sampling-factor 4:2:0 -strip /tmp/foo.bmp jpg:-')
|
|
138
|
+
.with('convert -resize 80.0% -quality 85 -sampling-factor 4:2:0 -strip \'/tmp/foo.bmp\' jpg:-')
|
|
139
139
|
transform
|
|
140
140
|
end
|
|
141
141
|
end
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'ruby-vips'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
module Vips
|
|
7
|
+
class Image
|
|
8
|
+
# Intentionally blank.
|
|
9
|
+
#
|
|
10
|
+
# This prevents uninitialized constant errors if vips
|
|
11
|
+
# is not installed.
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
RSpec.describe Riiif::VipsTransformer do
|
|
17
|
+
let(:channels) { 'rgb' }
|
|
18
|
+
|
|
19
|
+
let(:path) { "path/to/image.tif" }
|
|
20
|
+
|
|
21
|
+
let(:image) { double('Vips Image', has_alpha?: false) }
|
|
22
|
+
|
|
23
|
+
let(:image_info) do
|
|
24
|
+
double({ height: 376,
|
|
25
|
+
width: 500,
|
|
26
|
+
format: 'jpg',
|
|
27
|
+
channels: channels })
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
let(:target) { 'jpg' }
|
|
31
|
+
|
|
32
|
+
let(:transformation) do
|
|
33
|
+
IIIF::Image::Transformation.new(region: region,
|
|
34
|
+
size: size,
|
|
35
|
+
rotation: rotation,
|
|
36
|
+
format: target)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Default/Placeholder values that should be modified in tests
|
|
40
|
+
let(:size) { IIIF::Image::Size::Full.new }
|
|
41
|
+
let(:region) { IIIF::Image::Region::Full.new }
|
|
42
|
+
let(:rotation) { 0 }
|
|
43
|
+
|
|
44
|
+
before do
|
|
45
|
+
allow(Vips::Image).to receive(:new_from_file).and_return(image)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe '#initialize' do
|
|
49
|
+
let(:path) { Pathname.new("path/to/image.tif") }
|
|
50
|
+
|
|
51
|
+
it 'normalizes pathnames to strings' do
|
|
52
|
+
expect(Vips::Image).to receive(:new_from_file).with("path/to/image.tif")
|
|
53
|
+
described_class.new(path, image_info, transformation)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#transform' do
|
|
58
|
+
subject { described_class.new(path, image_info, transformation).transform }
|
|
59
|
+
before { allow(image).to receive(:write_to_buffer) }
|
|
60
|
+
after { subject }
|
|
61
|
+
|
|
62
|
+
context 'when requesting jpg format with default options' do
|
|
63
|
+
it 'writes to jpg format' do
|
|
64
|
+
expect(image).to receive(:write_to_buffer).with(".jpg[Q=85,optimize-coding,strip]")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context 'when requesting png format with default options' do
|
|
69
|
+
let(:target) { 'png' }
|
|
70
|
+
|
|
71
|
+
it 'writes to png format' do
|
|
72
|
+
expect(image).to receive(:write_to_buffer).with(".png[Q=85,strip]")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context 'when requesting jpeg format for a png' do
|
|
77
|
+
let(:image) { double('Vips Image', has_alpha?: true) }
|
|
78
|
+
|
|
79
|
+
it 'writes to png anyway to preserve transparency' do
|
|
80
|
+
expect(image).to receive(:write_to_buffer).with(".png[Q=85,strip]")
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context 'with subsampling turned off' do
|
|
85
|
+
subject { described_class.new(path, image_info, transformation, subsample: false).transform }
|
|
86
|
+
|
|
87
|
+
it 'does not subsample' do
|
|
88
|
+
expect(image).to receive(:write_to_buffer).with(".jpg[Q=85,optimize-coding,strip,no-subsample]")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context 'when specifying compression factor' do
|
|
93
|
+
subject { described_class.new(path, image_info, transformation, compression: 90).transform }
|
|
94
|
+
|
|
95
|
+
it 'compresses to the correct quality' do
|
|
96
|
+
expect(image).to receive(:write_to_buffer).with(".jpg[Q=90,optimize-coding,strip]")
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context 'when strip_metadata is false' do
|
|
101
|
+
subject { described_class.new(path, image_info, transformation, strip_metadata: false).transform }
|
|
102
|
+
|
|
103
|
+
it 'does not strip metadata' do
|
|
104
|
+
expect(image).to receive(:write_to_buffer).with(".jpg[Q=85,optimize-coding]")
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe '#transform_image' do
|
|
110
|
+
subject { described_class.new(path, image_info, transformation).send(:transform_image) }
|
|
111
|
+
|
|
112
|
+
before do
|
|
113
|
+
allow(image).to receive_messages(crop: image, resize: image, rotate: image, thumbnail_image: image, colourspace: image)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe 'resize' do
|
|
117
|
+
context 'when specifing full size' do
|
|
118
|
+
it 'does not resize' do
|
|
119
|
+
expect(image).not_to receive(:resize)
|
|
120
|
+
expect(image).not_to receive(:thumbnail_image)
|
|
121
|
+
subject
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
context 'when specifing percent size' do
|
|
126
|
+
let(:size) { IIIF::Image::Size::Percent.new(50) }
|
|
127
|
+
|
|
128
|
+
it 'resizes the image' do
|
|
129
|
+
expect(image).to receive(:resize).with(50.0)
|
|
130
|
+
expect(image).not_to receive(:thumbnail_image)
|
|
131
|
+
subject
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
context 'when specifing float percent size' do
|
|
136
|
+
let(:size) { IIIF::Image::Size::Percent.new(12.5) }
|
|
137
|
+
|
|
138
|
+
it 'resizes the image' do
|
|
139
|
+
expect(image).to receive(:resize).with(12.5)
|
|
140
|
+
expect(image).not_to receive(:thumbnail_image)
|
|
141
|
+
subject
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
context 'when specifying width and/or height' do
|
|
146
|
+
context 'when specifing w, size' do
|
|
147
|
+
let(:size) { IIIF::Image::Size::Width.new(300) }
|
|
148
|
+
|
|
149
|
+
before { allow(image).to receive(:width).and_return(600) }
|
|
150
|
+
|
|
151
|
+
it 'resizes the image to 300px wide, maintaining aspect ratio' do
|
|
152
|
+
expect(image).to receive(:resize).with(0.5)
|
|
153
|
+
subject
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
context 'when specifing ,h size' do
|
|
158
|
+
let(:size) { IIIF::Image::Size::Height.new(200) }
|
|
159
|
+
|
|
160
|
+
before { allow(image).to receive(:height).and_return(500) }
|
|
161
|
+
|
|
162
|
+
it 'resizes the image to 300px high, maintaining aspect ratio' do
|
|
163
|
+
expect(image).to receive(:resize).with(0.4)
|
|
164
|
+
subject
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
context 'when specifing absolute w,h size' do
|
|
169
|
+
let(:size) { IIIF::Image::Size::Absolute.new(200, 300) }
|
|
170
|
+
|
|
171
|
+
it 'resizes the image, ignoring aspect ratio' do
|
|
172
|
+
expect(image).to receive(:thumbnail_image).with(200, height: 300, size: :force)
|
|
173
|
+
subject
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
context 'when specifing bestfit (!w,h) size' do
|
|
178
|
+
let(:size) { IIIF::Image::Size::BestFit.new(200, 300) }
|
|
179
|
+
|
|
180
|
+
it 'resizes the image so that the width and height are equal or less than the requested value' do
|
|
181
|
+
expect(image).to receive(:thumbnail_image).with(200, height: 300)
|
|
182
|
+
subject
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
describe 'crop' do
|
|
189
|
+
after { subject }
|
|
190
|
+
|
|
191
|
+
context 'when specifing full size' do
|
|
192
|
+
let(:region) { IIIF::Image::Region::Full.new }
|
|
193
|
+
|
|
194
|
+
it 'does not crop' do
|
|
195
|
+
expect(image).not_to receive(:crop)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
context 'when specifing absolute geometry' do
|
|
200
|
+
let(:region) { IIIF::Image::Region::Absolute.new(80, 15, 60, 75) }
|
|
201
|
+
|
|
202
|
+
it 'crops to that region' do
|
|
203
|
+
expect(image).to receive(:crop).with(80, 15, 60, 75)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
context 'when specifing percent geometry' do
|
|
208
|
+
let(:region) { IIIF::Image::Region::Percent.new(10, 10, 80, 70) }
|
|
209
|
+
before { allow(image_info).to receive_messages(width: 100, height: 100, format: 'jpeg', channels: channels) }
|
|
210
|
+
|
|
211
|
+
it 'crops to that region' do
|
|
212
|
+
expect(image).to receive(:crop).with(10, 10, 80, 70)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
context 'when specifing square geometry' do
|
|
217
|
+
let(:region) { IIIF::Image::Region::Square.new }
|
|
218
|
+
|
|
219
|
+
it 'crops a square the size of the shortest edge' do
|
|
220
|
+
expect(image).to receive(:crop).with(62, 0, 376, 376)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
describe 'rotate' do
|
|
226
|
+
after { subject }
|
|
227
|
+
|
|
228
|
+
context 'when no rotation (0) is specified' do
|
|
229
|
+
it 'does not rotate' do
|
|
230
|
+
expect(image).not_to receive(:rotate)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
context 'when rotation is specified' do
|
|
235
|
+
let(:rotation) { 45 }
|
|
236
|
+
|
|
237
|
+
it 'rotates the image' do
|
|
238
|
+
expect(image).to receive(:rotate).with(45)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
describe 'colourspace' do
|
|
244
|
+
after { subject }
|
|
245
|
+
|
|
246
|
+
context 'when quality is default or color' do
|
|
247
|
+
it 'leaves the image in color' do
|
|
248
|
+
expect(image).not_to receive(:colourspace).with(:b_w)
|
|
249
|
+
expect(image).not_to receive(:>)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
context 'when quality is gray' do
|
|
254
|
+
let(:transformation) { IIIF::Image::Transformation.new(region: region, size: size, rotation: rotation, quality: 'gray') }
|
|
255
|
+
|
|
256
|
+
it 'makes the image grayscale' do
|
|
257
|
+
expect(image).to receive(:colourspace).with(:b_w)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
context 'when quality is bitonal' do
|
|
262
|
+
let(:transformation) { IIIF::Image::Transformation.new(region: region, size: size, rotation: rotation, quality: 'bitonal') }
|
|
263
|
+
|
|
264
|
+
it 'makes the image bitonal' do
|
|
265
|
+
expect(image).to receive(:colourspace).with(:b_w)
|
|
266
|
+
expect(image).to receive(:>).with(200)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|