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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/ruby.yml +39 -0
  4. data/.rubocop_todo.yml +115 -16
  5. data/Gemfile +2 -12
  6. data/README.md +29 -9
  7. data/app/controllers/riiif/images_controller.rb +1 -1
  8. data/app/extractors/riiif/abstract_info_extractor.rb +12 -0
  9. data/app/{services → extractors}/riiif/image_magick_info_extractor.rb +2 -7
  10. data/app/extractors/riiif/vips_info_extractor.rb +22 -0
  11. data/app/models/riiif/file.rb +11 -24
  12. data/app/models/riiif/image.rb +0 -3
  13. data/app/resolvers/riiif/abstract_file_system_resolver.rb +1 -11
  14. data/app/resolvers/riiif/akubra_system_file_resolver.rb +1 -0
  15. data/app/resolvers/riiif/file_system_file_resolver.rb +1 -1
  16. data/app/resolvers/riiif/http_file_resolver.rb +11 -3
  17. data/app/services/riiif/crop.rb +40 -0
  18. data/app/services/riiif/imagemagick_command_factory.rb +1 -1
  19. data/app/services/riiif/vips_resize.rb +47 -0
  20. data/app/transformers/riiif/vips_transformer.rb +103 -0
  21. data/docs/vips_comparison.md +257 -0
  22. data/lib/riiif/engine.rb +14 -0
  23. data/lib/riiif/version.rb +1 -1
  24. data/lib/riiif.rb +14 -0
  25. data/riiif.gemspec +9 -7
  26. data/spec/controllers/riiif/images_controller_spec.rb +8 -8
  27. data/spec/extractors/riiif/image_magick_info_extractor_spec.rb +31 -0
  28. data/spec/extractors/riiif/vips_info_extractor_spec.rb +76 -0
  29. data/spec/fixtures/test.jpg +0 -0
  30. data/spec/fixtures/test.png +0 -0
  31. data/spec/fixtures/test.tif +0 -0
  32. data/spec/models/riiif/file_spec.rb +35 -0
  33. data/spec/models/riiif/file_system_file_resolver_spec.rb +7 -0
  34. data/spec/models/riiif/http_file_resolver_spec.rb +1 -1
  35. data/spec/models/riiif/image_spec.rb +27 -21
  36. data/spec/spec_helper.rb +0 -6
  37. data/spec/test_app_templates/lib/generators/test_app_generator.rb +6 -1
  38. data/spec/transformers/riiif/kakadu_transformer_spec.rb +8 -8
  39. data/spec/transformers/riiif/vips_transformer_spec.rb +271 -0
  40. metadata +49 -19
  41. 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
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Riiif::HTTPFileResolver do
3
+ describe Riiif::HttpFileResolver do
4
4
  subject { described_class.new }
5
5
 
6
6
  before do
@@ -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 HTTPFileResolver' do
48
+ context 'using HttpFileResolver' do
43
49
  before do
44
- described_class.file_resolver = Riiif::HTTPFileResolver.new
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,11 +1,5 @@
1
- require 'coveralls'
2
1
  require 'simplecov'
3
2
 
4
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
5
- SimpleCov::Formatter::HTMLFormatter,
6
- Coveralls::SimpleCov::Formatter
7
- ])
8
-
9
3
  SimpleCov.start('rails')
10
4
 
11
5
  require 'engine_cart'
@@ -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