hydra-derivatives 3.1.3 → 3.1.4
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.yml +66 -0
- data/.rubocop_todo.yml +29 -0
- data/Gemfile +2 -0
- data/Rakefile +19 -4
- data/VERSION +1 -1
- data/lib/hydra/derivatives.rb +5 -5
- data/lib/hydra/derivatives/config.rb +7 -8
- data/lib/hydra/derivatives/logger.rb +2 -5
- data/lib/hydra/derivatives/processors/audio.rb +0 -1
- data/lib/hydra/derivatives/processors/document.rb +33 -18
- data/lib/hydra/derivatives/processors/ffmpeg.rb +2 -3
- data/lib/hydra/derivatives/processors/full_text.rb +1 -1
- data/lib/hydra/derivatives/processors/image.rb +39 -36
- data/lib/hydra/derivatives/processors/jpeg2k_image.rb +91 -89
- data/lib/hydra/derivatives/processors/processor.rb +1 -1
- data/lib/hydra/derivatives/processors/raw_image.rb +25 -25
- data/lib/hydra/derivatives/processors/shell_based_processor.rb +24 -32
- data/lib/hydra/derivatives/processors/video.rb +0 -2
- data/lib/hydra/derivatives/processors/video/config.rb +2 -4
- data/lib/hydra/derivatives/processors/video/processor.rb +24 -24
- data/lib/hydra/derivatives/runners/full_text_extract.rb +0 -1
- data/lib/hydra/derivatives/runners/image_derivatives.rb +0 -1
- data/lib/hydra/derivatives/runners/runner.rb +4 -7
- data/lib/hydra/derivatives/services/persist_basic_contained_output_file_service.rb +0 -1
- data/lib/hydra/derivatives/services/persist_output_file_service.rb +11 -13
- data/lib/hydra/derivatives/services/retrieve_source_file_service.rb +0 -1
- data/lib/hydra/derivatives/services/tempfile_service.rb +4 -5
- data/spec/processors/document_spec.rb +41 -0
- data/spec/processors/{full_text.rb → full_text_spec.rb} +10 -19
- data/spec/processors/image_spec.rb +28 -21
- data/spec/processors/jpeg2k_spec.rb +1 -2
- data/spec/processors/processor_spec.rb +2 -3
- data/spec/processors/shell_based_processor_spec.rb +1 -1
- data/spec/processors/video_spec.rb +4 -4
- data/spec/services/audio_derivatives_spec.rb +5 -5
- data/spec/services/persist_basic_contained_output_file_service_spec.rb +4 -6
- data/spec/services/retrieve_source_file_service_spec.rb +7 -7
- data/spec/services/tempfile_service_spec.rb +4 -2
- data/spec/spec_helper.rb +5 -3
- data/spec/units/config_spec.rb +5 -7
- data/spec/units/derivatives_spec.rb +8 -21
- data/spec/units/logger_spec.rb +7 -10
- data/spec/units/transcoding_spec.rb +111 -61
- metadata +9 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f9fe68ff09c49cf6061b2ef193ad428302dfac25
|
|
4
|
+
data.tar.gz: 1c0d21de93c9616a4d97cf4b8f6c46f68a66d96e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62bc8d5e8265a6f85214a530af6a6d36469473db17485b36a1cd622822ae591208ea5fa994ff2b23b4c1f5ab31d20131deb98061348941c47599be0ca403a2ee
|
|
7
|
+
data.tar.gz: d74669b4b6ec7c9b713ee33b72f35e7d43f4acfbc44a675ef162b6e8194c2df0363f17d9da958441209587118cdaa34b2d7c35688752a489acc6d563a1b76994
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require: rubocop-rspec
|
|
2
|
+
inherit_from: .rubocop_todo.yml
|
|
3
|
+
|
|
4
|
+
AllCops:
|
|
5
|
+
DisplayCopNames: true
|
|
6
|
+
Include:
|
|
7
|
+
- '**/Rakefile'
|
|
8
|
+
Exclude:
|
|
9
|
+
- 'vendor/**/*'
|
|
10
|
+
- 'spec/internal/bin/*'
|
|
11
|
+
- 'spec/internal/db/schema.rb'
|
|
12
|
+
|
|
13
|
+
Metrics/LineLength:
|
|
14
|
+
Enabled: false
|
|
15
|
+
|
|
16
|
+
Style/CollectionMethods:
|
|
17
|
+
PreferredMethods:
|
|
18
|
+
collect: 'map'
|
|
19
|
+
collect!: 'map!'
|
|
20
|
+
inject: 'reduce'
|
|
21
|
+
detect: 'find'
|
|
22
|
+
find_all: 'select'
|
|
23
|
+
|
|
24
|
+
Style/ClassAndModuleChildren:
|
|
25
|
+
Enabled: false
|
|
26
|
+
|
|
27
|
+
Style/Documentation:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
30
|
+
Style/StringLiterals:
|
|
31
|
+
Enabled: false
|
|
32
|
+
|
|
33
|
+
Style/SignalException:
|
|
34
|
+
Enabled: false
|
|
35
|
+
|
|
36
|
+
Style/IndentationConsistency:
|
|
37
|
+
EnforcedStyle: rails
|
|
38
|
+
|
|
39
|
+
Style/PredicateName:
|
|
40
|
+
Exclude:
|
|
41
|
+
- spec/services/tempfile_service_spec.rb
|
|
42
|
+
|
|
43
|
+
RSpec/ExampleWording:
|
|
44
|
+
CustomTransform:
|
|
45
|
+
be: is
|
|
46
|
+
have: has
|
|
47
|
+
not: does not
|
|
48
|
+
NOT: does NOT
|
|
49
|
+
IgnoredWords:
|
|
50
|
+
- only
|
|
51
|
+
|
|
52
|
+
RSpec/FilePath:
|
|
53
|
+
Enabled: false
|
|
54
|
+
|
|
55
|
+
RSpec/InstanceVariable:
|
|
56
|
+
Enabled: false
|
|
57
|
+
|
|
58
|
+
RSpec/DescribeClass:
|
|
59
|
+
Exclude:
|
|
60
|
+
- spec/units/config_spec.rb
|
|
61
|
+
- spec/units/transcoding_spec.rb
|
|
62
|
+
|
|
63
|
+
RSpec/AnyInstance:
|
|
64
|
+
Exclude:
|
|
65
|
+
- spec/processors/image_spec.rb
|
|
66
|
+
- spec/units/transcoding_spec.rb
|
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# List of files that we ought to fix based on what Rubocop is complaining about them
|
|
2
|
+
|
|
3
|
+
Metrics/CyclomaticComplexity:
|
|
4
|
+
Exclude:
|
|
5
|
+
- spec/units/transcoding_spec.rb
|
|
6
|
+
|
|
7
|
+
Metrics/MethodLength:
|
|
8
|
+
Exclude:
|
|
9
|
+
- lib/hydra/derivatives/config.rb
|
|
10
|
+
- lib/hydra/derivatives/processors/document.rb
|
|
11
|
+
- lib/hydra/derivatives/processors/jpeg2k_image.rb
|
|
12
|
+
- lib/hydra/derivatives/processors/shell_based_processor.rb
|
|
13
|
+
- lib/hydra/derivatives/processors/video/processor.rb
|
|
14
|
+
- lib/hydra/derivatives/services/tempfile_service.rb
|
|
15
|
+
- spec/units/transcoding_spec.rb
|
|
16
|
+
|
|
17
|
+
Metrics/ClassLength:
|
|
18
|
+
Exclude:
|
|
19
|
+
- lib/hydra/derivatives/processors/jpeg2k_image.rb
|
|
20
|
+
|
|
21
|
+
Metrics/AbcSize:
|
|
22
|
+
Exclude:
|
|
23
|
+
- lib/hydra/derivatives/processors/document.rb
|
|
24
|
+
- lib/hydra/derivatives/processors/full_text.rb
|
|
25
|
+
- lib/hydra/derivatives/processors/jpeg2k_image.rb
|
|
26
|
+
- lib/hydra/derivatives/processors/shell_based_processor.rb
|
|
27
|
+
- lib/hydra/derivatives/services/persist_basic_contained_output_file_service.rb
|
|
28
|
+
- lib/hydra/derivatives/services/tempfile_service.rb
|
|
29
|
+
- spec/units/transcoding_spec.rb
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
|
@@ -10,11 +10,26 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
10
10
|
require 'solr_wrapper/rake_task'
|
|
11
11
|
require 'fcrepo_wrapper'
|
|
12
12
|
require 'active_fedora/rake_support'
|
|
13
|
+
require 'rubocop/rake_task'
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
namespace :derivatives do
|
|
16
|
+
desc 'Run style checker'
|
|
17
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
|
18
|
+
task.requires << 'rubocop-rspec'
|
|
19
|
+
task.fail_on_error = true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
RSpec::Core::RakeTask.new(:rspec)
|
|
23
|
+
|
|
24
|
+
desc 'Start up Solr & Fedora and run tests'
|
|
25
|
+
task :spec do
|
|
26
|
+
with_test_server do
|
|
27
|
+
Rake::Task['derivatives:rspec'].invoke
|
|
28
|
+
end
|
|
18
29
|
end
|
|
19
30
|
end
|
|
31
|
+
|
|
32
|
+
desc 'Run continuous integration build'
|
|
33
|
+
task ci: ['derivatives:rubocop', 'derivatives:spec']
|
|
34
|
+
|
|
20
35
|
task default: :ci
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.1.
|
|
1
|
+
3.1.4
|
data/lib/hydra/derivatives.rb
CHANGED
|
@@ -47,13 +47,13 @@ module Hydra
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
[:ffmpeg_path, :libreoffice_path, :temp_file_base, :fits_path, :kdu_compress_path,
|
|
50
|
-
|
|
50
|
+
:kdu_compress_recipes, :enable_ffmpeg, :source_file_service, :output_file_service].each do |method|
|
|
51
51
|
module_eval <<-RUBY
|
|
52
|
-
def self.#{method
|
|
53
|
-
config.#{method
|
|
52
|
+
def self.#{method}
|
|
53
|
+
config.#{method}
|
|
54
54
|
end
|
|
55
|
-
def self.#{method
|
|
56
|
-
config.#{method
|
|
55
|
+
def self.#{method}= val
|
|
56
|
+
config.#{method}= val
|
|
57
57
|
end
|
|
58
58
|
RUBY
|
|
59
59
|
end
|
|
@@ -3,9 +3,9 @@ require 'tmpdir'
|
|
|
3
3
|
module Hydra
|
|
4
4
|
module Derivatives
|
|
5
5
|
class Config
|
|
6
|
-
attr_writer :ffmpeg_path, :libreoffice_path, :temp_file_base,
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
attr_writer :ffmpeg_path, :libreoffice_path, :temp_file_base,
|
|
7
|
+
:source_file_service, :output_file_service, :fits_path,
|
|
8
|
+
:enable_ffmpeg, :kdu_compress_path, :kdu_compress_recipes
|
|
9
9
|
|
|
10
10
|
def ffmpeg_path
|
|
11
11
|
@ffmpeg_path ||= 'ffmpeg'
|
|
@@ -41,7 +41,7 @@ module Hydra
|
|
|
41
41
|
|
|
42
42
|
def kdu_compress_recipes
|
|
43
43
|
@kdu_compress_recipes ||= {
|
|
44
|
-
default_color: %
|
|
44
|
+
default_color: %(-rate 2.4,1.48331273,.91673033,.56657224,.35016049,.21641118,.13374944,.08266171
|
|
45
45
|
-jp2_space sRGB
|
|
46
46
|
-double_buffering 10
|
|
47
47
|
-num_threads 4
|
|
@@ -54,8 +54,8 @@ module Hydra
|
|
|
54
54
|
Corder=RPCL
|
|
55
55
|
ORGgen_plt=yes
|
|
56
56
|
ORGtparts=R
|
|
57
|
-
"Stiles={1024,1024}"
|
|
58
|
-
default_grey: %
|
|
57
|
+
"Stiles={1024,1024}" ).gsub(/\s+/, " ").strip,
|
|
58
|
+
default_grey: %(-rate 2.4,1.48331273,.91673033,.56657224,.35016049,.21641118,.13374944,.08266171
|
|
59
59
|
-jp2_space sLUM
|
|
60
60
|
-double_buffering 10
|
|
61
61
|
-num_threads 4
|
|
@@ -68,10 +68,9 @@ module Hydra
|
|
|
68
68
|
Corder=RPCL
|
|
69
69
|
ORGgen_plt=yes
|
|
70
70
|
ORGtparts=R
|
|
71
|
-
"Stiles={1024,1024}"
|
|
71
|
+
"Stiles={1024,1024}" ).gsub(/\s+/, " ").strip
|
|
72
72
|
}
|
|
73
73
|
end
|
|
74
|
-
|
|
75
74
|
end
|
|
76
75
|
end
|
|
77
76
|
end
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
module Hydra::Derivatives
|
|
2
2
|
class Logger
|
|
3
|
-
|
|
4
3
|
class << self
|
|
5
|
-
|
|
6
|
-
def method_missing method_name, *arguments, &block
|
|
4
|
+
def method_missing(method_name, *arguments, &block)
|
|
7
5
|
logger.send(method_name, *arguments, &block)
|
|
8
6
|
rescue
|
|
9
7
|
super
|
|
10
8
|
end
|
|
11
9
|
|
|
12
|
-
def respond_to?(method_name,
|
|
10
|
+
def respond_to?(method_name, _include_private = false)
|
|
13
11
|
logger.respond_to? method_name
|
|
14
12
|
end
|
|
15
13
|
|
|
@@ -18,7 +16,6 @@ module Hydra::Derivatives
|
|
|
18
16
|
def logger
|
|
19
17
|
ActiveFedora::Base.logger || ::Logger.new(STDOUT)
|
|
20
18
|
end
|
|
21
|
-
|
|
22
19
|
end
|
|
23
20
|
end
|
|
24
21
|
end
|
|
@@ -2,27 +2,42 @@ module Hydra::Derivatives::Processors
|
|
|
2
2
|
class Document < Processor
|
|
3
3
|
include ShellBasedProcessor
|
|
4
4
|
|
|
5
|
-
def self.encode(path,
|
|
6
|
-
format = File.extname(output_file).sub('.', '')
|
|
7
|
-
outdir = File.dirname(output_file)
|
|
5
|
+
def self.encode(path, format, outdir)
|
|
8
6
|
execute "#{Hydra::Derivatives.libreoffice_path} --invisible --headless --convert-to #{format} --outdir #{outdir} #{path}"
|
|
9
7
|
end
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
File.unlink(temp_file)
|
|
19
|
-
else
|
|
20
|
-
self.class.encode(source_path, options, output_file(file_suffix))
|
|
21
|
-
new_output = File.join(Hydra::Derivatives.temp_file_base, [directives.fetch(:label).to_s, file_suffix].join('.'))
|
|
22
|
-
end
|
|
23
|
-
out_file = File.open(new_output, "rb")
|
|
24
|
-
output_file_service.call(out_file, directives)
|
|
25
|
-
File.unlink(out_file)
|
|
9
|
+
# Converts the document to the format specified in the directives hash.
|
|
10
|
+
# TODO: file_suffix and options are passed from ShellBasedProcessor.process but are not needed.
|
|
11
|
+
# A refactor could simplify this.
|
|
12
|
+
def encode_file(_file_suffix, _options = {})
|
|
13
|
+
convert_to_format
|
|
14
|
+
ensure
|
|
15
|
+
FileUtils.rm_f(converted_file)
|
|
26
16
|
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# For jpeg files, a pdf is created from the original source and then passed to the Image processor class
|
|
21
|
+
# so we can get a better conversion with resizing options. Otherwise, the ::encode method is used.
|
|
22
|
+
def convert_to_format
|
|
23
|
+
if directives.fetch(:format) == "jpg"
|
|
24
|
+
Hydra::Derivatives::Processors::Image.new(converted_file, directives).process
|
|
25
|
+
else
|
|
26
|
+
output_file_service.call(File.read(converted_file), directives)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def converted_file
|
|
31
|
+
@converted_file ||= if directives.fetch(:format) == "jpg"
|
|
32
|
+
convert_to("pdf")
|
|
33
|
+
else
|
|
34
|
+
convert_to(directives.fetch(:format))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def convert_to(format)
|
|
39
|
+
self.class.encode(source_path, format, Hydra::Derivatives.temp_file_base)
|
|
40
|
+
File.join(Hydra::Derivatives.temp_file_base, [File.basename(source_path, ".*"), format].join('.'))
|
|
41
|
+
end
|
|
27
42
|
end
|
|
28
43
|
end
|
|
@@ -3,14 +3,13 @@ module Hydra::Derivatives::Processors
|
|
|
3
3
|
module Ffmpeg
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
|
-
INPUT_OPTIONS
|
|
7
|
-
OUTPUT_OPTIONS
|
|
6
|
+
INPUT_OPTIONS = :input_options
|
|
7
|
+
OUTPUT_OPTIONS = :output_options
|
|
8
8
|
|
|
9
9
|
included do
|
|
10
10
|
include ShellBasedProcessor
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
|
|
14
13
|
module ClassMethods
|
|
15
14
|
def encode(path, options, output_file)
|
|
16
15
|
inopts = options[INPUT_OPTIONS] ||= "-y"
|
|
@@ -36,7 +36,7 @@ module Hydra::Derivatives::Processors
|
|
|
36
36
|
|
|
37
37
|
# @return [Hash] the request headers to send to the Solr extract service
|
|
38
38
|
def request_headers
|
|
39
|
-
{ Faraday::Request::UrlEncoded::CONTENT_TYPE =>
|
|
39
|
+
{ Faraday::Request::UrlEncoded::CONTENT_TYPE => mime_type.to_s,
|
|
40
40
|
Faraday::Adapter::CONTENT_LENGTH => original_size.to_s }
|
|
41
41
|
end
|
|
42
42
|
|
|
@@ -5,54 +5,57 @@ module Hydra::Derivatives::Processors
|
|
|
5
5
|
class_attribute :timeout
|
|
6
6
|
|
|
7
7
|
def process
|
|
8
|
-
timeout ? process_with_timeout :
|
|
8
|
+
timeout ? process_with_timeout : create_resized_image
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def process_with_timeout
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
rescue Timeout::Error => ex
|
|
12
|
+
Timeout.timeout(timeout) { create_resized_image }
|
|
13
|
+
rescue Timeout::Error
|
|
16
14
|
raise Hydra::Derivatives::TimeoutError, "Unable to process image derivative\nThe command took longer than #{timeout} seconds to execute"
|
|
17
15
|
end
|
|
18
16
|
|
|
19
|
-
def process_without_timeout
|
|
20
|
-
format = directives.fetch(:format)
|
|
21
|
-
name = directives.fetch(:label, format)
|
|
22
|
-
destination_name = output_filename_for(name)
|
|
23
|
-
size = directives.fetch(:size, nil)
|
|
24
|
-
quality = directives.fetch(:quality, nil)
|
|
25
|
-
create_resized_image(destination_name, size, format, quality)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
17
|
protected
|
|
29
18
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
# When resizing images, it is necessary to flatten any layers, otherwise the background
|
|
20
|
+
# may be completely black. This happens especially with PDFs. See #110
|
|
21
|
+
def create_resized_image
|
|
22
|
+
create_image do |xfrm|
|
|
23
|
+
if size
|
|
24
|
+
xfrm.flatten
|
|
25
|
+
xfrm.resize(size)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
33
28
|
end
|
|
34
|
-
end
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
def create_image
|
|
31
|
+
xfrm = load_image_transformer
|
|
32
|
+
yield(xfrm) if block_given?
|
|
33
|
+
xfrm.format(directives.fetch(:format))
|
|
34
|
+
xfrm.quality(quality.to_s) if quality
|
|
35
|
+
write_image(xfrm)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def write_image(xfrm)
|
|
39
|
+
output_io = StringIO.new
|
|
40
|
+
xfrm.write(output_io)
|
|
41
|
+
output_io.rewind
|
|
42
|
+
output_file_service.call(output_io, directives)
|
|
43
|
+
end
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
# Override this method if you want a different transformer, or need to load the
|
|
46
|
+
# raw image from a different source (e.g. external file)
|
|
47
|
+
def load_image_transformer
|
|
48
|
+
MiniMagick::Image.open(source_path)
|
|
49
|
+
end
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
end
|
|
51
|
+
private
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
def size
|
|
54
|
+
directives.fetch(:size, nil)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def quality
|
|
58
|
+
directives.fetch(:quality, nil)
|
|
59
|
+
end
|
|
57
60
|
end
|
|
58
61
|
end
|
|
@@ -1,15 +1,97 @@
|
|
|
1
1
|
require 'mini_magick'
|
|
2
2
|
require 'nokogiri'
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
module Hydra::Derivatives::Processors
|
|
6
5
|
class Jpeg2kImage < Processor
|
|
7
6
|
include ShellBasedProcessor
|
|
8
7
|
|
|
8
|
+
class << self
|
|
9
|
+
def srgb_profile_path
|
|
10
|
+
File.join [
|
|
11
|
+
File.expand_path('../../../../', __FILE__),
|
|
12
|
+
'color_profiles',
|
|
13
|
+
'sRGB_IEC61966-2-1_no_black_scaling.icc'
|
|
14
|
+
]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def kdu_compress_recipe(args, quality, long_dim)
|
|
18
|
+
if args[:recipe].is_a? Symbol
|
|
19
|
+
recipe = [args[:recipe].to_s, quality].join('_').to_sym
|
|
20
|
+
if Hydra::Derivatives.kdu_compress_recipes.key? recipe
|
|
21
|
+
return Hydra::Derivatives.kdu_compress_recipes[recipe]
|
|
22
|
+
else
|
|
23
|
+
ActiveFedora::Base.logger.warn "No JP2 recipe for :#{args[:recipe]} ('#{recipe}') found in configuration. Using best guess."
|
|
24
|
+
return calculate_recipe(args, quality, long_dim)
|
|
25
|
+
end
|
|
26
|
+
elsif args[:recipe].is_a? String
|
|
27
|
+
return args[:recipe]
|
|
28
|
+
else
|
|
29
|
+
return calculate_recipe(args, quality, long_dim)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def calculate_recipe(args, quality, long_dim)
|
|
34
|
+
levels_arg = args.fetch(:levels, level_count_for_size(long_dim))
|
|
35
|
+
rates_arg = layer_rates(args.fetch(:layers, 8), args.fetch(:compression, 10))
|
|
36
|
+
tile_size = args.fetch(:tile_size, 1024)
|
|
37
|
+
tiles_arg = "#{tile_size},#{tile_size}"
|
|
38
|
+
jp2_space_arg = quality == 'gray' ? 'sLUM' : 'sRGB'
|
|
39
|
+
|
|
40
|
+
%(-rate #{rates_arg}
|
|
41
|
+
-jp2_space #{jp2_space_arg}
|
|
42
|
+
-double_buffering 10
|
|
43
|
+
-num_threads 4
|
|
44
|
+
-no_weights
|
|
45
|
+
Clevels=#{levels_arg}
|
|
46
|
+
"Stiles={#{tiles_arg}}"
|
|
47
|
+
"Cblk={64,64}"
|
|
48
|
+
Cuse_sop=yes
|
|
49
|
+
Cuse_eph=yes
|
|
50
|
+
Corder=RPCL
|
|
51
|
+
ORGgen_plt=yes
|
|
52
|
+
ORGtparts=R ).gsub(/\s+/, " ").strip
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def level_count_for_size(long_dim)
|
|
56
|
+
levels = 0
|
|
57
|
+
level_size = long_dim
|
|
58
|
+
while level_size >= 96
|
|
59
|
+
level_size /= 2
|
|
60
|
+
levels += 1
|
|
61
|
+
end
|
|
62
|
+
levels - 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def layer_rates(layer_count, compression_numerator)
|
|
66
|
+
# e.g. if compression_numerator = 10 then compression is 10:1
|
|
67
|
+
rates = []
|
|
68
|
+
cmp = 24.0 / compression_numerator
|
|
69
|
+
layer_count.times do
|
|
70
|
+
rates << cmp
|
|
71
|
+
cmp = (cmp / 1.618).round(8)
|
|
72
|
+
end
|
|
73
|
+
rates.map(&:to_s).join(',')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
protected
|
|
77
|
+
|
|
78
|
+
def encode(path, recipe, output_file)
|
|
79
|
+
kdu_compress = Hydra::Derivatives.kdu_compress_path
|
|
80
|
+
execute "#{kdu_compress} -quiet -i #{path} -o #{output_file} #{recipe}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def tmp_file(ext)
|
|
84
|
+
Dir::Tmpname.create(['sufia', ext], Hydra::Derivatives.temp_file_base) {}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def long_dim(image)
|
|
88
|
+
[image[:width], image[:height]].max
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
9
92
|
def process
|
|
10
93
|
image = MiniMagick::Image.open(source_path)
|
|
11
94
|
quality = image['%[channels]'] == 'gray' ? 'gray' : 'color'
|
|
12
|
-
name = directives.fetch(:label)
|
|
13
95
|
long_dim = self.class.long_dim(image)
|
|
14
96
|
file_path = self.class.tmp_file('.tif')
|
|
15
97
|
to_srgb = directives.fetch(:to_srgb, true)
|
|
@@ -22,7 +104,7 @@ module Hydra::Derivatives::Processors
|
|
|
22
104
|
File.unlink(file_path) unless file_path.nil?
|
|
23
105
|
end
|
|
24
106
|
|
|
25
|
-
def encode_file(recipe, opts={})
|
|
107
|
+
def encode_file(recipe, opts = {})
|
|
26
108
|
output_file = self.class.tmp_file('.jp2')
|
|
27
109
|
if opts[:file_path]
|
|
28
110
|
self.class.encode(opts[:file_path], recipe, output_file)
|
|
@@ -36,94 +118,14 @@ module Hydra::Derivatives::Processors
|
|
|
36
118
|
end
|
|
37
119
|
|
|
38
120
|
protected
|
|
39
|
-
def preprocess(image, opts={})
|
|
40
|
-
# resize: <geometry>, to_srgb: <bool>, src_quality: 'color'|'gray'
|
|
41
|
-
image.combine_options do |c|
|
|
42
|
-
c.resize(opts[:resize]) if opts[:resize]
|
|
43
|
-
c.profile self.class.srgb_profile_path if opts[:src_quality] == 'color' && opts[:to_srgb]
|
|
44
|
-
end
|
|
45
|
-
image
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def self.encode(path, recipe, output_file)
|
|
49
|
-
kdu_compress = Hydra::Derivatives.kdu_compress_path
|
|
50
|
-
execute "#{kdu_compress} -quiet -i #{path} -o #{output_file} #{recipe}"
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def self.srgb_profile_path
|
|
54
|
-
File.join [
|
|
55
|
-
File.expand_path('../../../../', __FILE__),
|
|
56
|
-
'color_profiles',
|
|
57
|
-
'sRGB_IEC61966-2-1_no_black_scaling.icc'
|
|
58
|
-
]
|
|
59
|
-
end
|
|
60
121
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
[image[:width], image[:height]].max
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def self.kdu_compress_recipe(args, quality, long_dim)
|
|
70
|
-
if args[:recipe].is_a? Symbol
|
|
71
|
-
recipe = [args[:recipe].to_s, quality].join('_').to_sym
|
|
72
|
-
if Hydra::Derivatives.kdu_compress_recipes.has_key? recipe
|
|
73
|
-
return Hydra::Derivatives.kdu_compress_recipes[recipe]
|
|
74
|
-
else
|
|
75
|
-
ActiveFedora::Base.logger.warn "No JP2 recipe for :#{args[:recipe].to_s} ('#{recipe}') found in configuration. Using best guess."
|
|
76
|
-
return calculate_recipe(args,quality,long_dim)
|
|
122
|
+
def preprocess(image, opts = {})
|
|
123
|
+
# resize: <geometry>, to_srgb: <bool>, src_quality: 'color'|'gray'
|
|
124
|
+
image.combine_options do |c|
|
|
125
|
+
c.resize(opts[:resize]) if opts[:resize]
|
|
126
|
+
c.profile self.class.srgb_profile_path if opts[:src_quality] == 'color' && opts[:to_srgb]
|
|
77
127
|
end
|
|
78
|
-
|
|
79
|
-
return args[:recipe]
|
|
80
|
-
else
|
|
81
|
-
return calculate_recipe(args, quality, long_dim)
|
|
128
|
+
image
|
|
82
129
|
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def self.calculate_recipe(args, quality, long_dim)
|
|
86
|
-
levels_arg = args.fetch(:levels, level_count_for_size(long_dim))
|
|
87
|
-
rates_arg = layer_rates(args.fetch(:layers, 8), args.fetch(:compression, 10))
|
|
88
|
-
tile_size = args.fetch(:tile_size, 1024)
|
|
89
|
-
tiles_arg = "#{tile_size},#{tile_size}"
|
|
90
|
-
jp2_space_arg = quality == 'gray' ? 'sLUM' : 'sRGB'
|
|
91
|
-
|
|
92
|
-
%Q{-rate #{rates_arg}
|
|
93
|
-
-jp2_space #{jp2_space_arg}
|
|
94
|
-
-double_buffering 10
|
|
95
|
-
-num_threads 4
|
|
96
|
-
-no_weights
|
|
97
|
-
Clevels=#{levels_arg}
|
|
98
|
-
"Stiles={#{tiles_arg}}"
|
|
99
|
-
"Cblk={64,64}"
|
|
100
|
-
Cuse_sop=yes
|
|
101
|
-
Cuse_eph=yes
|
|
102
|
-
Corder=RPCL
|
|
103
|
-
ORGgen_plt=yes
|
|
104
|
-
ORGtparts=R }.gsub(/\s+/, " ").strip
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def self.level_count_for_size(long_dim)
|
|
108
|
-
levels = 0
|
|
109
|
-
level_size = long_dim
|
|
110
|
-
while level_size >= 96
|
|
111
|
-
level_size = level_size/2
|
|
112
|
-
levels+=1
|
|
113
|
-
end
|
|
114
|
-
levels-1
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def self.layer_rates(layer_count,compression_numerator)
|
|
118
|
-
#e.g. if compression_numerator = 10 then compression is 10:1
|
|
119
|
-
rates = []
|
|
120
|
-
cmp = 24.0/compression_numerator
|
|
121
|
-
layer_count.times do
|
|
122
|
-
rates << cmp
|
|
123
|
-
cmp = (cmp/1.618).round(8)
|
|
124
|
-
end
|
|
125
|
-
rates.map(&:to_s ).join(',')
|
|
126
|
-
end
|
|
127
|
-
|
|
128
130
|
end
|
|
129
131
|
end
|