riiif 1.7.1 → 2.0.0.beta1
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/.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
|