riiif 1.7.1 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +15 -41
- data/Gemfile +1 -37
- data/app/models/riiif/file.rb +19 -12
- data/app/models/riiif/image.rb +13 -12
- data/app/models/riiif/image_information.rb +4 -0
- data/app/models/riiif/transformation.rb +35 -0
- data/{lib → app/resolvers}/riiif/abstract_file_system_resolver.rb +0 -0
- data/{lib → app/resolvers}/riiif/akubra_system_file_resolver.rb +0 -0
- data/{lib → app/resolvers}/riiif/file_system_file_resolver.rb +0 -0
- data/{lib → app/resolvers}/riiif/http_file_resolver.rb +0 -0
- data/app/services/riiif/command_runner.rb +2 -0
- data/app/services/riiif/crop.rb +54 -0
- data/app/services/riiif/imagemagick_command_factory.rb +20 -19
- data/app/services/riiif/imagemagick_transformer.rb +8 -0
- data/app/services/riiif/kakadu_command_factory.rb +63 -0
- data/app/services/riiif/link_name_service.rb +8 -0
- data/{lib → app/services}/riiif/nil_authorization_service.rb +0 -0
- data/app/services/riiif/option_decoder.rb +11 -11
- data/app/services/riiif/region/absolute.rb +23 -0
- data/app/services/riiif/region/full.rb +23 -0
- data/app/services/riiif/region/percentage.rb +68 -0
- data/app/services/riiif/region/square.rb +45 -0
- data/app/services/riiif/resize.rb +45 -0
- data/app/services/riiif/size/absolute.rb +39 -0
- data/app/services/riiif/size/best_fit.rb +18 -0
- data/app/services/riiif/size/full.rb +17 -0
- data/app/services/riiif/size/height.rb +24 -0
- data/app/services/riiif/size/percent.rb +44 -0
- data/app/services/riiif/size/width.rb +24 -0
- data/app/transformers/riiif/abstract_transformer.rb +30 -0
- data/app/transformers/riiif/imagemagick_transformer.rb +8 -0
- data/app/transformers/riiif/kakadu_transformer.rb +39 -0
- data/lib/riiif.rb +4 -7
- data/lib/riiif/engine.rb +4 -3
- data/lib/riiif/version.rb +1 -1
- data/riiif.gemspec +1 -1
- data/spec/models/riiif/image_spec.rb +6 -3
- data/spec/models/riiif/transformation_spec.rb +42 -0
- data/spec/services/riiif/imagemagick_command_factory_spec.rb +6 -4
- data/spec/services/riiif/kakadu_command_factory_spec.rb +85 -0
- data/spec/services/riiif/region/absolute_spec.rb +17 -0
- data/spec/services/riiif/size/absolute_spec.rb +17 -0
- data/spec/services/riiif/size/height_spec.rb +13 -0
- data/spec/services/riiif/size/width_spec.rb +13 -0
- data/spec/transformers/riiif/kakadu_transformer_spec.rb +143 -0
- metadata +45 -22
- data/app/services/riiif/region/imagemagick/absolute_decoder.rb +0 -21
- data/app/services/riiif/region/imagemagick/full_decoder.rb +0 -14
- data/app/services/riiif/region/imagemagick/percentage_decoder.rb +0 -33
- data/app/services/riiif/region/imagemagick/square_decoder.rb +0 -25
- data/app/services/riiif/size/imagemagick/absolute_decoder.rb +0 -20
- data/app/services/riiif/size/imagemagick/best_fit_decoder.rb +0 -19
- data/app/services/riiif/size/imagemagick/full_decoder.rb +0 -14
- data/app/services/riiif/size/imagemagick/height_decoder.rb +0 -19
- data/app/services/riiif/size/imagemagick/percent_decoder.rb +0 -19
- data/app/services/riiif/size/imagemagick/width_decoder.rb +0 -19
@@ -0,0 +1,44 @@
|
|
1
|
+
module Riiif
|
2
|
+
module Size
|
3
|
+
# The width and height of the returned image is scaled to n% of the width and height
|
4
|
+
# of the extracted region. The aspect ratio of the returned image is the same as that
|
5
|
+
# of the extracted region.
|
6
|
+
class Percent < Resize
|
7
|
+
def initialize(info, percentage)
|
8
|
+
@image_info = info
|
9
|
+
@percentage = percentage
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :percentage
|
13
|
+
|
14
|
+
# @return [String] a resize directive for imagemagick to use
|
15
|
+
def to_imagemagick
|
16
|
+
"#{percentage}%"
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer] the height in pixels
|
20
|
+
def height
|
21
|
+
percent_of(image_info.height)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Integer] the width in pixels
|
25
|
+
def width
|
26
|
+
percent_of(image_info.width)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [Integer] factor number of times to reduce by 1/2
|
30
|
+
def reduce(factor)
|
31
|
+
pct = percentage.to_f * 2**factor
|
32
|
+
Percent.new(image_info, pct)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# @param [Integer] value a value to convert to the percentage
|
38
|
+
# @return [Float]
|
39
|
+
def percent_of(value)
|
40
|
+
value * Integer(percentage).to_f / 100
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Riiif
|
2
|
+
module Size
|
3
|
+
# The image or region should be scaled so that its width is exactly equal
|
4
|
+
# to the provided parameter, and the height will be a calculated value that
|
5
|
+
# maintains the aspect ratio of the extracted region
|
6
|
+
class Width < Resize
|
7
|
+
def initialize(info, width)
|
8
|
+
@image_info = info
|
9
|
+
@width = width.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [String] a resize directive for imagemagick to use
|
13
|
+
def to_imagemagick
|
14
|
+
@width.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :width
|
18
|
+
|
19
|
+
def height
|
20
|
+
width * image_info.height / image_info.width
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Riiif
|
2
|
+
# Transforms an image using a backend
|
3
|
+
class AbstractTransformer
|
4
|
+
# @param path [String] The path of the source image file
|
5
|
+
# @param image_info [ImageInformation] information about the source
|
6
|
+
# @param [Transformation] transformation
|
7
|
+
def self.transform(path, image_info, transformation)
|
8
|
+
new(path, image_info, transformation).transform
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(path, image_info, transformation)
|
12
|
+
@path = path
|
13
|
+
@image_info = image_info
|
14
|
+
@transformation = transformation
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :path, :image_info, :transformation
|
18
|
+
|
19
|
+
def transform
|
20
|
+
execute(command_builder.command)
|
21
|
+
end
|
22
|
+
|
23
|
+
def command_builder
|
24
|
+
@command_builder ||= command_factory.new(path, image_info, transformation)
|
25
|
+
end
|
26
|
+
|
27
|
+
delegate :execute, to: Riiif::CommandRunner
|
28
|
+
private :execute
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Riiif
|
2
|
+
# Transforms an image using Kakadu
|
3
|
+
class KakaduTransformer < AbstractTransformer
|
4
|
+
def command_factory
|
5
|
+
KakaduCommandFactory
|
6
|
+
end
|
7
|
+
|
8
|
+
def transform
|
9
|
+
with_tempfile do |file_name|
|
10
|
+
execute(command_builder.command(file_name))
|
11
|
+
post_process(file_name, command_builder.reduction_factor)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_tempfile
|
16
|
+
Tempfile.open(['riiif-intermediate', '.bmp']) do |f|
|
17
|
+
yield f.path
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# The data we get back from kdu_expand is a bmp and we need to change it
|
22
|
+
# to the requested format by calling Imagemagick.
|
23
|
+
def post_process(intermediate_file, reduction_factor)
|
24
|
+
# Calculate a new set of transforms with respect to reduction_factor
|
25
|
+
transformation = if reduction_factor
|
26
|
+
self.transformation.without_crop(image_info).reduce(reduction_factor)
|
27
|
+
else
|
28
|
+
self.transformation.without_crop(image_info)
|
29
|
+
end
|
30
|
+
Riiif::File.new(intermediate_file).extract(transformation, image_info)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def tmp_path
|
36
|
+
@link_path ||= LinkNameService.create
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/riiif.rb
CHANGED
@@ -3,13 +3,7 @@ require 'deprecation'
|
|
3
3
|
require 'riiif/engine'
|
4
4
|
module Riiif
|
5
5
|
extend ActiveSupport::Autoload
|
6
|
-
autoload :Image
|
7
|
-
autoload :AbstractFileSystemResolver
|
8
|
-
autoload :FileSystemFileResolver
|
9
|
-
autoload :HTTPFileResolver
|
10
6
|
autoload :Routes
|
11
|
-
autoload :AkubraSystemFileResolver
|
12
|
-
autoload :NilAuthorizationService
|
13
7
|
|
14
8
|
class Error < RuntimeError; end
|
15
9
|
class InvalidAttributeError < Error; end
|
@@ -18,7 +12,10 @@ module Riiif
|
|
18
12
|
# This error is raised when Riiif can't convert an image
|
19
13
|
class ConversionError < Error; end
|
20
14
|
|
21
|
-
Transformation = Struct.new(:crop, :size, :quality, :rotation, :format)
|
22
15
|
mattr_accessor :not_found_image # the image to use when a lookup fails
|
23
16
|
mattr_accessor :unauthorized_image # the image to use when a user doesn't have access
|
17
|
+
|
18
|
+
def self.kakadu_enabled?
|
19
|
+
Engine.config.kakadu_enabled
|
20
|
+
end
|
24
21
|
end
|
data/lib/riiif/engine.rb
CHANGED
@@ -5,8 +5,9 @@ module Riiif
|
|
5
5
|
# How long to cache the tiles for.
|
6
6
|
config.cache_duration_in_days = 3
|
7
7
|
|
8
|
-
config.action_dispatch.rescue_responses
|
9
|
-
|
10
|
-
|
8
|
+
config.action_dispatch.rescue_responses['Riiif::ImageNotFoundError'] = :not_found
|
9
|
+
|
10
|
+
# Set to true to use kdu for jp2000 source images
|
11
|
+
config.kakadu_enabled = false
|
11
12
|
end
|
12
13
|
end
|
data/lib/riiif/version.rb
CHANGED
data/riiif.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_dependency 'deprecation', '>= 1.0.0'
|
23
23
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
24
24
|
spec.add_development_dependency 'rake'
|
25
|
-
spec.add_development_dependency 'engine_cart', '~>
|
25
|
+
spec.add_development_dependency 'engine_cart', '~> 0.8'
|
26
26
|
spec.add_development_dependency 'rspec-rails'
|
27
27
|
spec.add_development_dependency 'sqlite3'
|
28
28
|
spec.add_development_dependency 'rubocop', '~> 0.47.1'
|
@@ -15,11 +15,14 @@ RSpec.describe Riiif::Image do
|
|
15
15
|
|
16
16
|
describe 'happy path' do
|
17
17
|
before do
|
18
|
-
|
19
|
-
.with("convert -quality 85 -sampling-factor 4:2:0 -strip #{filename} jpg:-")
|
20
|
-
.and_return('imagedata')
|
18
|
+
allow(image.info_service).to receive(:call).and_return({})
|
21
19
|
end
|
20
|
+
|
22
21
|
it 'renders' do
|
22
|
+
expect(Riiif::CommandRunner).to receive(:execute)
|
23
|
+
.with("convert -quality 85 -sampling-factor 4:2:0 -strip #{filename} jpg:-")
|
24
|
+
.and_return('imagedata')
|
25
|
+
|
23
26
|
expect(subject.render('size' => 'full', format: 'jpg')).to eq 'imagedata'
|
24
27
|
end
|
25
28
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Riiif::Transformation do
|
6
|
+
subject(:transformation) do
|
7
|
+
Riiif::Transformation.new(region,
|
8
|
+
size,
|
9
|
+
quality,
|
10
|
+
rotation,
|
11
|
+
fmt)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:region) { Riiif::Region::Full.new(image_info) }
|
15
|
+
let(:size) { Riiif::Size::Percent.new(image_info, 20) }
|
16
|
+
let(:quality) { nil }
|
17
|
+
let(:rotation) { nil }
|
18
|
+
let(:fmt) { nil }
|
19
|
+
let(:image_info) { double('Image info', height: 4381, width: 6501) }
|
20
|
+
|
21
|
+
describe 'reduce' do
|
22
|
+
subject { transformation.reduce(factor) }
|
23
|
+
context 'when reduced by 2' do
|
24
|
+
let(:factor) { 2 }
|
25
|
+
let(:size) { Riiif::Size::Percent.new(image_info, 20) }
|
26
|
+
|
27
|
+
it 'downsamples the size' do
|
28
|
+
expect(subject.size).to be_kind_of Riiif::Size::Percent
|
29
|
+
expect(subject.size.percentage).to eq 80.0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'without_crop' do
|
35
|
+
let(:region) { Riiif::Region::Absolute.new(image_info, 5, 6, 7, 8) }
|
36
|
+
|
37
|
+
subject { transformation.without_crop(image_info) }
|
38
|
+
it 'nullifies the crop' do
|
39
|
+
expect(subject.crop).to be_kind_of Riiif::Region::Full
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -2,13 +2,15 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Riiif::ImagemagickCommandFactory do
|
4
4
|
let(:path) { 'foo.tiff' }
|
5
|
+
let(:info) { double('foo') }
|
5
6
|
|
6
|
-
describe '.
|
7
|
-
subject {
|
7
|
+
describe '.command' do
|
8
|
+
subject { instance.command }
|
9
|
+
let(:instance) { described_class.new(path, info, transformation) }
|
8
10
|
|
9
11
|
let(:transformation) do
|
10
|
-
Riiif::Transformation.new(
|
11
|
-
|
12
|
+
Riiif::Transformation.new(Riiif::Region::Full.new(info),
|
13
|
+
Riiif::Size::Full.new,
|
12
14
|
'quality',
|
13
15
|
'rotation',
|
14
16
|
format)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Riiif::KakaduCommandFactory do
|
6
|
+
subject(:instance) { described_class.new(path, info, transformation) }
|
7
|
+
|
8
|
+
let(:info) { double(:info) }
|
9
|
+
let(:path) { 'foo.jp2' }
|
10
|
+
let(:region) { Riiif::Region::Full.new(info) }
|
11
|
+
let(:size) { Riiif::Size::Full.new }
|
12
|
+
let(:quality) { nil }
|
13
|
+
let(:rotation) { nil }
|
14
|
+
let(:fmt) { nil }
|
15
|
+
|
16
|
+
let(:transformation) do
|
17
|
+
Riiif::Transformation.new(region,
|
18
|
+
size,
|
19
|
+
quality,
|
20
|
+
rotation,
|
21
|
+
fmt)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#command' do
|
25
|
+
subject { instance.command '/tmp/bar.bmp' }
|
26
|
+
|
27
|
+
context 'with a full size image' do
|
28
|
+
it { is_expected.to eq 'kdu_expand -quiet -i foo.jp2 -num_threads 4 -o /tmp/bar.bmp' }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#region' do
|
33
|
+
subject { instance.send(:region) }
|
34
|
+
let(:info) { double(height: 300, width: 300) }
|
35
|
+
|
36
|
+
context 'with a full' do
|
37
|
+
it { is_expected.to be nil }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with absolute' do
|
41
|
+
let(:region) { Riiif::Region::Absolute.new(info, 25, 75, 150, 100) }
|
42
|
+
it { is_expected.to eq ' -region "{0.25,0.08333333333333333},{0.3333333333333333,0.5}"' }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with a square' do
|
46
|
+
let(:region) { Riiif::Region::Square.new(info) }
|
47
|
+
it { is_expected.to eq ' -region "{0.0,0},{1.0,1.0}"' }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with a percentage' do
|
51
|
+
let(:region) { Riiif::Region::Percentage.new(info, 20, 30, 40, 50) }
|
52
|
+
it { is_expected.to eq ' -region "{0.3,0.2},{0.5,0.4}"' }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#reduction_factor' do
|
57
|
+
subject { instance.send(:reduction_factor) }
|
58
|
+
|
59
|
+
let(:info) { Riiif::ImageInformation.new(300, 300) }
|
60
|
+
|
61
|
+
context 'for a full size image' do
|
62
|
+
it { is_expected.to eq nil }
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when the aspect ratio is maintined for absolute' do
|
66
|
+
let(:size) { Riiif::Size::Absolute.new(info, 145, 145) }
|
67
|
+
it { is_expected.to eq 1 }
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when the aspect ratio is not-maintined' do
|
71
|
+
let(:size) { Riiif::Size::Absolute.new(info, 100, 145) }
|
72
|
+
it { is_expected.to eq nil }
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'when aspect ratio is maintained for 45 pct' do
|
76
|
+
let(:size) { Riiif::Size::Percent.new(info, 45) }
|
77
|
+
it { is_expected.to eq 1 }
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when aspect ratio is maintained for 20 pct' do
|
81
|
+
let(:size) { Riiif::Size::Percent.new(info, 20) }
|
82
|
+
it { is_expected.to eq 2 }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Riiif::Region::Absolute do
|
4
|
+
let(:image_info) { double }
|
5
|
+
|
6
|
+
context 'when initialized with strings' do
|
7
|
+
let(:instance) { described_class.new(image_info, '5', '15', '50', '100') }
|
8
|
+
|
9
|
+
it 'casts height to an integer' do
|
10
|
+
expect(instance.height).to eq 100
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'casts width to an integer' do
|
14
|
+
expect(instance.width).to eq 50
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Riiif::Size::Absolute do
|
4
|
+
let(:image_info) { double }
|
5
|
+
|
6
|
+
context 'when initialized with strings' do
|
7
|
+
let(:instance) { described_class.new(image_info, '50', '100') }
|
8
|
+
|
9
|
+
it 'casts height to an integer' do
|
10
|
+
expect(instance.height).to eq 100
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'casts width to an integer' do
|
14
|
+
expect(instance.width).to eq 50
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Riiif::Size::Height do
|
4
|
+
let(:image_info) { double }
|
5
|
+
|
6
|
+
context 'when initialized with strings' do
|
7
|
+
let(:instance) { described_class.new(image_info, '50') }
|
8
|
+
|
9
|
+
it 'casts height to an integer' do
|
10
|
+
expect(instance.height).to eq 50
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Riiif::Size::Width do
|
4
|
+
let(:image_info) { double }
|
5
|
+
|
6
|
+
context 'when initialized with strings' do
|
7
|
+
let(:instance) { described_class.new(image_info, '50') }
|
8
|
+
|
9
|
+
it 'casts height to an integer' do
|
10
|
+
expect(instance.width).to eq 50
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|