riiif 2.8.1 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c83cf037ced763741c5aa704db17564fd43ee2610866d27629b1de4700c518a
4
- data.tar.gz: 447135b1b851c29969cd42a91a51b26facb1084b47dc246422b11a15de0dcbc5
3
+ metadata.gz: 9993f1679d618e86c346172bc70c57947d1fcf8d06ee7c704981b099270fe605
4
+ data.tar.gz: dab42c9074cf26db432fca000121fcf54772cd03d80d05cbfbdb38ca9b53ae40
5
5
  SHA512:
6
- metadata.gz: 86952c0517dfd44217aee70ae4927209aaa7c4078c9142126a6f0a06fe831774371dffb0191c2719cb868249b98b324cbfbf7853c2518622bb67dc2d1afab182
7
- data.tar.gz: f315b4762cf831614a80728ac072678ea4ad9f1fc38cbc836cd7f6e3eaf6c2009b870682e0024d4cebca02c9da919a33dde3b66bcd98ac8da5f2f4d5691b23fd
6
+ metadata.gz: 162484c5fa19da91a13ba5168e0902f7ccaa315026299eca14f7556abee5b2c00d7d1667cd840dff77156089f355230f763460cc9a761071d9eb277c51394a74
7
+ data.tar.gz: 68d5b87acb4e5d19efff588dbdc5a352e839529be45bff33a58443497f0939dc94a30001ff95cb3be37d52c99090b5c3b49f70638902f047e8a78c9960e969ba
@@ -22,8 +22,11 @@ jobs:
22
22
  rails: ["8.1.1", "8.0.4", "7.2.3"]
23
23
  steps:
24
24
  - name: Install ImageMagick
25
- run: sudo apt install imagemagick
26
- - uses: actions/checkout@v6
25
+ run: |
26
+ sudo rm -f /var/lib/man-db/auto-update
27
+ sudo apt-get update
28
+ sudo apt-get install -y --no-install-recommends imagemagick
29
+ - uses: actions/checkout@v7
27
30
  - name: Set up Ruby
28
31
  uses: ruby/setup-ruby@v1
29
32
  with:
data/README.md CHANGED
@@ -102,6 +102,16 @@ To use [libvips](https://www.libvips.org/) instead of ImageMagick
102
102
  Riiif::Engine.config.use_vips = true
103
103
  ```
104
104
 
105
+ ### EXIF orientation
106
+
107
+ By default, derivatives and `info.json` dimensions use the raw stored pixels and ignore the EXIF Orientation tag. To apply it (as browsers and thumbnailers do):
108
+
109
+ ```ruby
110
+ Riiif::Engine.config.autorotate = true
111
+ ```
112
+
113
+ This works with ImageMagick and libvips. Cached derivatives and `info.json` need to be cleared after changing it.
114
+
105
115
  ## Usage
106
116
 
107
117
  Add the routes to your application by inserting the following line into `config/routes.rb`
@@ -6,7 +6,7 @@ module Riiif
6
6
 
7
7
  def extract
8
8
  height, width, format, channels = Riiif::CommandRunner.execute(
9
- "#{external_command} -format '%h %w %m %[channels]' '#{@path}[0]'"
9
+ "#{external_command}#{' -auto-orient' if Riiif.autorotate?} -format '%h %w %m %[channels]' '#{@path}[0]'"
10
10
  ).split(' ')
11
11
 
12
12
  {
@@ -6,16 +6,17 @@ module Riiif
6
6
  self.external_command = 'vipsheader'
7
7
 
8
8
  def extract
9
- attributes = Riiif::CommandRunner.execute("#{external_command} '#{@path}' -a")
10
- .split(/\n/)
11
- .map { |str| str.strip.split(': ', 2) }.to_h
12
- width, height = attributes.values_at("width", "height")
9
+ vipsloader = Riiif::CommandRunner.execute(
10
+ "#{external_command} -f vips-loader '#{@path}'"
11
+ ).strip
12
+ image = ::Vips::Image.new_from_file(@path.to_s)
13
+ image = image.autorot if Riiif.autorotate?
13
14
 
14
15
  {
15
- height: Integer(height),
16
- width: Integer(width),
17
- format: attributes["vips-loader"].match?("pngload") ? "PNG" : "JPEG",
18
- channels: ::Vips::Image.new_from_file(@path.to_s).has_alpha? ? "srgba" : "srgb"
16
+ height: image.height,
17
+ width: image.width,
18
+ format: vipsloader.match?("pngload") ? "PNG" : "JPEG",
19
+ channels: image.has_alpha? ? "srgba" : "srgb"
19
20
  }
20
21
  end
21
22
  end
@@ -29,6 +29,7 @@ module Riiif
29
29
  def command
30
30
  [
31
31
  external_command,
32
+ auto_orient,
32
33
  crop,
33
34
  size,
34
35
  rotation,
@@ -79,6 +80,10 @@ module Riiif
79
80
  end
80
81
  end
81
82
 
83
+ def auto_orient
84
+ ' -auto-orient' if Riiif.autorotate?
85
+ end
86
+
82
87
  def crop
83
88
  directive = Crop.new(transformation.region, info).to_imagemagick
84
89
  " -crop #{directive}" if directive
@@ -15,6 +15,7 @@ module Riiif
15
15
  def initialize(path, image_info, transformation, compression: 85, subsample: true, strip_metadata: true)
16
16
  super(path, image_info, transformation)
17
17
  @image = ::Vips::Image.new_from_file(path.to_s)
18
+ @image = @image.autorot if Riiif.autorotate?
18
19
  @compression = compression
19
20
  @subsample = subsample
20
21
  @strip_metadata = strip_metadata
data/lib/riiif/engine.rb CHANGED
@@ -15,6 +15,9 @@ module Riiif
15
15
  # https://www.libvips.org/
16
16
  config.use_vips = false
17
17
 
18
+ # Set to true to apply EXIF orientation to derivatives and reported dimensions
19
+ config.autorotate = false
20
+
18
21
  config.before_configuration do
19
22
  # see https://github.com/fxn/zeitwerk#for_gem
20
23
  # We put a generator into LOCAL APP lib/generators, so tell
data/lib/riiif/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Riiif
2
- VERSION = '2.8.1'.freeze
2
+ VERSION = '2.9.0'.freeze
3
3
  end
data/lib/riiif.rb CHANGED
@@ -32,4 +32,8 @@ module Riiif
32
32
  def self.use_vips?
33
33
  Engine.config.use_vips
34
34
  end
35
+
36
+ def self.autorotate?
37
+ Engine.config.autorotate
38
+ end
35
39
  end
data/riiif.gemspec CHANGED
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency 'sqlite3'
31
31
  spec.add_development_dependency 'bixby', '~> 5.0'
32
32
  spec.add_development_dependency 'simplecov'
33
+ spec.add_development_dependency 'webmock'
33
34
  end
@@ -3,6 +3,15 @@ RSpec.describe Riiif::ImageMagickInfoExtractor do
3
3
  expect(described_class.external_command).to eq "identify"
4
4
  end
5
5
 
6
+ context 'when autorotate is enabled' do
7
+ before { allow(Riiif).to receive(:autorotate?).and_return(true) }
8
+
9
+ it 'asks identify to auto-orient' do
10
+ expect(Riiif::CommandRunner).to receive(:execute).with(/\Aidentify -auto-orient /).and_return('376 500 JPEG srgb')
11
+ expect(described_class.new('x.jpg').extract).to include(width: 500, height: 376)
12
+ end
13
+ end
14
+
6
15
  context 'with a jpg' do
7
16
  let(:image) { Rails.root.join("spec", "fixtures", "test.jpg") }
8
17
 
@@ -6,15 +6,9 @@ RSpec.describe Riiif::VipsInfoExtractor do
6
6
  allow(Vips::Image).to receive(:new_from_file).and_return(image)
7
7
  end
8
8
 
9
- let(:image) { double(has_alpha?: false) }
9
+ let(:image) { double(has_alpha?: false, width: 500, height: 376) }
10
10
 
11
- let(:fake_info) do
12
- "width: 500
13
- height: 376
14
- interpretation: srgb
15
- filename: spec/fixtures/test.tif
16
- vips-loader: tiffload"
17
- end
11
+ let(:fake_info) { "tiffload" }
18
12
 
19
13
  it 'uses vipsheader as its external command' do
20
14
  expect(described_class.external_command).to eq "vipsheader"
@@ -31,45 +25,30 @@ RSpec.describe Riiif::VipsInfoExtractor do
31
25
  end
32
26
  end
33
27
 
34
- context 'on a file with transparency' do
35
- let(:image) { double(has_alpha?: true) }
28
+ context 'when autorotate is enabled' do
29
+ let(:oriented) { double(has_alpha?: false, width: 376, height: 500) }
36
30
 
37
- let(:fake_info) do
38
- "width: 50
39
- height: 50
40
- interpretation: srgb
41
- filename: spec/fixtures/test.tif
42
- vips-loader: pngload"
31
+ before do
32
+ allow(Riiif).to receive(:autorotate?).and_return(true)
33
+ allow(image).to receive(:autorot).and_return(oriented)
43
34
  end
44
35
 
45
- it 'returns the extracted attributes' do
46
- expect(described_class.new(image).extract).to eq({
47
- height: 50,
48
- width: 50,
49
- format: "PNG",
50
- channels: "srgba"
51
- })
36
+ it 'reports the dimensions after orientation is applied' do
37
+ expect(described_class.new("path/to/image.jpg").extract).to include(width: 376, height: 500)
52
38
  end
53
39
  end
54
40
 
55
- context 'on a JPEG file with extra EXIF metadata' do
56
- let(:image) { double(has_alpha?: false) }
41
+ context 'on a file with transparency' do
42
+ let(:image) { double(has_alpha?: true, width: 50, height: 50) }
57
43
 
58
- let(:fake_info) do
59
- "width: 150
60
- height: 150
61
- interpretation: srgb
62
- filename: spec/fixtures/test.jpeg
63
- vips-loader: jpegload
64
- exif-ifd0-Artist: University Library (Digital Object: Digital Media Group)"
65
- end
44
+ let(:fake_info) { "pngload" }
66
45
 
67
46
  it 'returns the extracted attributes' do
68
47
  expect(described_class.new(image).extract).to eq({
69
- height: 150,
70
- width: 150,
71
- format: "JPEG",
72
- channels: "srgb"
48
+ height: 50,
49
+ width: 50,
50
+ format: "PNG",
51
+ channels: "srgba"
73
52
  })
74
53
  end
75
54
  end
@@ -58,8 +58,21 @@ RSpec.describe Riiif::Image do
58
58
 
59
59
  describe 'get info' do
60
60
  subject { described_class.new('Cave_26,_Ajanta') }
61
- it 'is easy' do
62
- expect(subject.info).to eq Riiif::ImageInformation.new(width: 600, height: 390)
61
+
62
+ before do
63
+ stub_request(:get, "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Cave_26,_Ajanta.jpg/600px-Cave_26,_Ajanta.jpg")
64
+ .with(
65
+ headers: {
66
+ 'Accept' => '*/*',
67
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
68
+ 'User-Agent' => 'Ruby'
69
+ }
70
+ )
71
+ .to_return(status: 200, body: File.binread('spec/fixtures/test.jpg'), headers: {})
72
+ end
73
+
74
+ it 'gets the info' do
75
+ expect(subject.info).to eq Riiif::ImageInformation.new(width: 300, height: 397)
63
76
  end
64
77
  end
65
78
 
@@ -16,6 +16,21 @@ RSpec.describe Riiif::ImagemagickCommandFactory do
16
16
  format: target)
17
17
  end
18
18
 
19
+ context 'with autorotate' do
20
+ subject { described_class.new(tiff, info, transformation).command }
21
+
22
+ let(:source) { 'tif' }
23
+ let(:target) { 'jpg' }
24
+
25
+ it { is_expected.not_to match(/-auto-orient/) }
26
+
27
+ context 'when enabled' do
28
+ before { allow(Riiif).to receive(:autorotate?).and_return(true) }
29
+
30
+ it { is_expected.to match(/convert -auto-orient /) }
31
+ end
32
+ end
33
+
19
34
  context "when the target format is jpeg" do
20
35
  subject { described_class.new(tiff, info, transformation).command }
21
36
 
data/spec/spec_helper.rb CHANGED
@@ -7,6 +7,8 @@ ENV['RAILS_ENV'] ||= 'test'
7
7
 
8
8
  EngineCart.load_application!
9
9
  require 'rspec/rails'
10
+ require 'webmock/rspec'
11
+ WebMock.disable_net_connect!
10
12
 
11
13
  RSpec.configure do |config|
12
14
  config.run_all_when_everything_filtered = true
@@ -49,9 +49,27 @@ RSpec.describe Riiif::VipsTransformer do
49
49
  let(:path) { Pathname.new("path/to/image.tif") }
50
50
 
51
51
  it 'normalizes pathnames to strings' do
52
- expect(Vips::Image).to receive(:new_from_file).with("path/to/image.tif")
52
+ expect(Vips::Image).to receive(:new_from_file).with("path/to/image.tif").and_return(image)
53
53
  described_class.new(path, image_info, transformation)
54
54
  end
55
+
56
+ it 'does not apply EXIF orientation by default' do
57
+ expect(image).not_to receive(:autorot)
58
+ expect(described_class.new(path, image_info, transformation).image).to eq image
59
+ end
60
+
61
+ context 'when autorotate is enabled' do
62
+ let(:oriented) { double('Oriented Vips Image', has_alpha?: false) }
63
+
64
+ before do
65
+ allow(Riiif).to receive(:autorotate?).and_return(true)
66
+ allow(image).to receive(:autorot).and_return(oriented)
67
+ end
68
+
69
+ it 'applies the EXIF orientation to the source image' do
70
+ expect(described_class.new(path, image_info, transformation).image).to eq oriented
71
+ end
72
+ end
55
73
  end
56
74
 
57
75
  describe '#transform' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riiif
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.1
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
@@ -169,6 +169,20 @@ dependencies:
169
169
  - - ">="
170
170
  - !ruby/object:Gem::Version
171
171
  version: '0'
172
+ - !ruby/object:Gem::Dependency
173
+ name: webmock
174
+ requirement: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ type: :development
180
+ prerelease: false
181
+ version_requirements: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
172
186
  description: A IIIF image server
173
187
  email:
174
188
  - jcoyne85@stanford.edu
@@ -258,7 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
258
272
  - !ruby/object:Gem::Version
259
273
  version: '0'
260
274
  requirements: []
261
- rubygems_version: 4.0.7
275
+ rubygems_version: 4.0.22
262
276
  specification_version: 4
263
277
  summary: A Rails engine that support IIIF requests
264
278
  test_files: