hydra-derivatives 3.1.3 → 3.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|