assembly-image 1.9.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fee80ede73f89c00b4190c802537d9d92057958cd056b2f076c5e54294daa2a
4
- data.tar.gz: af6b7ca6d3600a051d2403bdfb72de29ebfab3ebe2e4296529e2cd10fb98662e
3
+ metadata.gz: 9379ec53a26a08578c11910512bd73045366fc337fcafb0c7eb1a247370566b1
4
+ data.tar.gz: 316fbef312cb027f4061bbe74dc2f8dce993a095d6779da100abeeecbf24b9da
5
5
  SHA512:
6
- metadata.gz: e380fd9bda9288ee8fabb74ab0ad7256c2623499044265b739b8bacc407f92ef97ee4102265ba2bbb414c89c9ead373c65cdc8df7726f055f524673d36322684
7
- data.tar.gz: 7e712a8bd31dd1728a0fc3b07a78c98cdd78a98fe0a2d5d38bd2960ceb48317b60dc3f0747011cb897603bd9d72558add2d0ab322eeb7246cc31d66f090a8e31
6
+ metadata.gz: 97cc720649bdc6e0bb6d7453afdefc7abe3f0931a45976d397f1f77fd2f2dacbcbd4991e64c0e4849a2ae61efa51d72d428ce9a8a15e9a125abdeefcb17b0d5b
7
+ data.tar.gz: 8e6580aaade8302319e7cbf41d56d8ac0ebe1a05db5607bed8580cebe8a3c87b5795cb90f50e2b9eb4b081d9048a600799b0e373badf811ff9a084e5f9af3fcc
data/.circleci/config.yml CHANGED
@@ -12,6 +12,9 @@ workflows:
12
12
  - run:
13
13
  name: Install exiftool
14
14
  command: curl -L http://cpanmin.us | perl - --sudo Image::ExifTool
15
+ - run:
16
+ name: Install libvips
17
+ command: sudo apt-get update && sudo apt-get install -y libvips
15
18
  - setup_remote_docker
16
19
  - run:
17
20
  name: Install Kakadu
data/.gitignore CHANGED
@@ -14,3 +14,5 @@ tmp
14
14
 
15
15
  coverage
16
16
  spec/examples.txt
17
+ spec/test_data/input/*
18
+ spec/test_data/output/*
data/.rubocop.yml CHANGED
@@ -22,9 +22,6 @@ Naming/FileName:
22
22
  Exclude:
23
23
  - lib/assembly-image.rb
24
24
 
25
- RSpec/ExampleLength:
26
- Max: 12
27
-
28
25
  Style/WordArray:
29
26
  Enabled: false
30
27
 
@@ -166,3 +163,6 @@ RSpec/ChangeByZero: # new in 2.11.0
166
163
  Enabled: true
167
164
  RSpec/VerifiedDoubleReference: # new in 2.10.0
168
165
  Enabled: true
166
+
167
+ Gemspec/DeprecatedAttributeAssignment: # new in 1.30
168
+ Enabled: true
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2022-02-28 19:49:08 UTC using RuboCop version 1.25.1.
3
+ # on 2022-06-16 22:15:07 UTC using RuboCop version 1.30.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -13,13 +13,23 @@ Gemspec/RequiredRubyVersion:
13
13
  Exclude:
14
14
  - 'assembly-image.gemspec'
15
15
 
16
- # Offense count: 3
17
- # Cop supports --auto-correct.
16
+ # Offense count: 2
17
+ # This cop supports safe autocorrection (--autocorrect).
18
18
  Lint/RedundantCopDisableDirective:
19
19
  Exclude:
20
20
  - 'lib/assembly-image/image.rb'
21
21
  - 'lib/assembly-image/images.rb'
22
22
 
23
+ # Offense count: 2
24
+ # Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
25
+ Metrics/AbcSize:
26
+ Max: 20
27
+
28
+ # Offense count: 14
29
+ # Configuration parameters: CountAsOne.
30
+ RSpec/ExampleLength:
31
+ Max: 15
32
+
23
33
  # Offense count: 2
24
34
  # Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly.
25
35
  # Include: **/*_spec*rb*, **/spec/**/*
@@ -30,7 +40,7 @@ RSpec/FilePath:
30
40
 
31
41
  # Offense count: 21
32
42
  RSpec/MultipleExpectations:
33
- Max: 10
43
+ Max: 13
34
44
 
35
45
  # Offense count: 5
36
46
  RSpec/UnspecifiedException:
@@ -44,7 +54,7 @@ Style/CombinableLoops:
44
54
  - 'spec/images_spec.rb'
45
55
 
46
56
  # Offense count: 1
47
- # Cop supports --auto-correct.
57
+ # This cop supports unsafe autocorrection (--autocorrect-all).
48
58
  Style/GlobalStdStream:
49
59
  Exclude:
50
60
  - 'lib/assembly-image/images.rb'
@@ -56,8 +66,8 @@ Style/OptionalBooleanParameter:
56
66
  Exclude:
57
67
  - 'lib/assembly-image/image.rb'
58
68
 
59
- # Offense count: 8
60
- # Cop supports --auto-correct-all.
69
+ # Offense count: 7
70
+ # This cop supports unsafe autocorrection (--autocorrect-all).
61
71
  # Configuration parameters: Mode.
62
72
  Style/StringConcatenation:
63
73
  Exclude:
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  ## Overview
9
9
  This gem contains classes used by the Stanford University Digital Library to
10
- perform image operations necessary for accessioning of content.
10
+ perform image operations necessary for accessioning of content.
11
11
 
12
12
  Requires image processing software - see PreRequisites section below.
13
13
 
@@ -69,33 +69,12 @@ http://kakadusoftware.com/downloads/
69
69
 
70
70
  NOTE: If you have upgrade to El Capitan on OS X, you will need to donwload and re-install the latest version of Kakadu, due to changes made with SIP. These changes moved the old executable binaries to an inaccessible location.
71
71
 
72
- ### Imagemagick
73
-
74
- #### RHEL 6
75
-
76
- The version of ImageMagick included with RHEL 6 has all of the dependency libraries included:
77
-
78
- ```bash
79
- yum install ImageMagick
80
- ```
81
- #### RHEL 5
82
-
83
- The version of ImageMagick included with RHEL 5 is too old and does not have all the proper binaries included/built:
84
-
85
- ```bash
86
- yum install lcms lcms-devel libjpeg libjpeg-devel libpng libpng-devel
87
- ```
88
- Required libraries from source:
89
- * libtiff (version 3.9.4 or higher)
90
-
91
- Build Imagemagick from source:
92
- http://www.imagemagick.org/download/ImageMagick.tar.gz
72
+ ### Libvips
93
73
 
94
74
  #### Mac
95
75
 
96
76
  ```bash
97
- brew install jasper libtiff
98
- brew install imagemagick --use-tiff --use-jpeg2000
77
+ brew install libvips
99
78
  ```
100
79
 
101
80
  ### Exiftool
@@ -4,7 +4,7 @@ $LOAD_PATH.push File.expand_path('lib', __dir__)
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'assembly-image'
7
- s.version = '1.9.0'
7
+ s.version = '2.0.0'
8
8
  s.authors = ['Peter Mangiafico', 'Renzo Sanchez-Silva', 'Monty Hindman', 'Tony Calavano']
9
9
  s.email = ['pmangiafico@stanford.edu']
10
10
  s.homepage = ''
@@ -13,14 +13,15 @@ Gem::Specification.new do |s|
13
13
  s.metadata['rubygems_mfa_required'] = 'true'
14
14
 
15
15
  s.files = `git ls-files`.split("\n")
16
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
16
  s.bindir = 'exe'
18
17
  s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
18
  s.require_paths = ['lib']
20
19
 
21
20
  s.add_dependency 'assembly-objectfile', '>= 1.6.4'
22
21
  s.add_dependency 'mini_exiftool', '>= 1.6', '< 3'
22
+ s.add_dependency 'ruby-vips', '>= 2.0'
23
23
 
24
+ s.add_development_dependency 'pry-byebug'
24
25
  s.add_development_dependency 'rake'
25
26
  s.add_development_dependency 'rspec', '~> 3.0'
26
27
  s.add_development_dependency 'rubocop'
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'ruby-vips'
3
4
  require 'assembly-objectfile'
4
5
  require_relative 'jp2_creator'
5
6
 
@@ -74,8 +75,7 @@ module Assembly
74
75
  def add_exif_profile_description(profile_name, force = false)
75
76
  if profile.nil? || force
76
77
  input_profile = profile_name.gsub(/[^[:alnum:]]/, '') # remove all non alpha-numeric characters, so we can get to a filename
77
- path_to_profiles = File.join(Assembly::PATH_TO_IMAGE_GEM, 'profiles')
78
- input_profile_file = File.join(path_to_profiles, "#{input_profile}.icc")
78
+ input_profile_file = File.join(PATH_TO_PROFILES, "#{input_profile}.icc")
79
79
  command = "exiftool '-icc_profile<=#{input_profile_file}' #{path}"
80
80
  result = `#{command} 2>&1`
81
81
  raise "profile addition command failed: #{command} with result #{result}" unless $CHILD_STATUS.success?
@@ -94,16 +94,6 @@ module Assembly
94
94
  File.extname(path).empty? ? "#{path}.jp2" : path.gsub(File.extname(path), '.jp2')
95
95
  end
96
96
 
97
- # Returns the full DPG equivalent jp2 path and filename that would match with the given image
98
- #
99
- # @return [string] full DPG equivalent jp2 path and filename
100
- # Example:
101
- # source_img=Assembly::Image.new('/input/path_to_file.tif')
102
- # puts source_img.jp2_filename # gives /input/path_to_file.jp2
103
- def dpg_jp2_filename
104
- jp2_filename.gsub('_00_', '_05_')
105
- end
106
-
107
97
  # Create a JP2 file for the current image.
108
98
  # Important note: this will not work for multipage TIFFs.
109
99
  #
@@ -37,8 +37,6 @@ module Assembly
37
37
 
38
38
  raise 'Input path does not exist' unless File.directory?(source)
39
39
 
40
- logger.debug "Source: #{source}"
41
-
42
40
  # iterate over input directory looking for tifs
43
41
  pattern = recursive ? "**/*.#{extension}" : "*.#{extension}*"
44
42
  Dir.glob(File.join(source, pattern)).each do |file|
@@ -81,9 +79,6 @@ module Assembly
81
79
  Dir.mkdir(output) unless File.directory?(output) # attemp to make output directory
82
80
  raise 'Output path does not exist or could not be created' unless File.directory?(output)
83
81
 
84
- logger.debug "Source: #{source}"
85
- logger.debug "Destination: #{output}"
86
-
87
82
  pattern = recursive ? "**/*.#{extension}" : "*.#{extension}*"
88
83
 
89
84
  # iterate over input directory looking for tifs
@@ -7,7 +7,7 @@ require 'English' # see https://github.com/rubocop-hq/rubocop/issues/1747 (not #
7
7
  module Assembly
8
8
  class Image < Assembly::ObjectFile
9
9
  # Creates jp2 derivatives
10
- class Jp2Creator # rubocop:disable Metrics/ClassLength
10
+ class Jp2Creator
11
11
  # Create a JP2 file for the current image.
12
12
  # Important note: this will not work for multipage TIFFs.
13
13
  #
@@ -68,6 +68,7 @@ module Assembly
68
68
 
69
69
  def jp2_create_command(source_path:, output:)
70
70
  options = []
71
+ # TODO: Consider using ruby-vips to determine the colorspace instead of relying on exif (which is done below)
71
72
  options << '-jp2_space sRGB' if image.samples_per_pixel == 3
72
73
  options += KDU_COMPRESS_DEFAULT_OPTIONS
73
74
  options << "Clayers=#{layers}"
@@ -82,20 +83,15 @@ module Assembly
82
83
 
83
84
  KDU_COMPRESS_DEFAULT_OPTIONS = [
84
85
  '-num_threads 2', # forces Kakadu to only use 2 threads
85
- '-precise', # forces the use of 32-bit representations
86
- '-no_weights', # minimization of the MSE over all reconstructed colour components
87
86
  '-quiet', # suppress informative messages.
88
87
  'Creversible=no', # Disable reversible compression
89
- 'Cmodes=BYPASS', #
90
88
  'Corder=RPCL', # R=resolution P=position C=component L=layer
91
89
  'Cblk=\\{64,64\\}', # code-block dimensions; 64x64 happens to also be the default
92
90
  'Cprecincts=\\{256,256\\},\\{256,256\\},\\{128,128\\}', # Precinct dimensions; 256x256 for the 2 highest resolution levels, defaults to 128x128 for the rest
93
- 'ORGgen_plt=yes', # Insert packet length information
94
- '-rate 1.5', # Ratio of compressed bits to the image size
91
+ '-rate -', # Ratio of compressed bits to the image size
95
92
  'Clevels=5' # Number of wavelet decomposition levels, or stages
96
93
  ].freeze
97
94
 
98
- # rubocop:disable Metrics/AbcSize
99
95
  def create_jp2_checks
100
96
  image.send(:check_for_file)
101
97
  raise 'input file is not a valid image, or is the wrong mimetype' unless image.jp2able?
@@ -104,46 +100,13 @@ module Assembly
104
100
  raise SecurityError, 'cannot recreate jp2 over itself' if overwrite? && image.mimetype == 'image/jp2' && output_path == image.path
105
101
  end
106
102
 
107
- # rubocop:disable Metrics/MethodLength
108
- def profile_conversion_switch(profile, tmp_folder:)
109
- path_to_profiles = File.join(Assembly::PATH_TO_IMAGE_GEM, 'profiles')
110
- # eventually we may allow the user to specify the output_profile...when we do, you can just uncomment this code
111
- # and update the tests that check for this
112
- output_profile = 'sRGBIEC6196621' # params[:output_profile] || 'sRGBIEC6196621'
113
- output_profile_file = File.join(path_to_profiles, "#{output_profile}.icc")
114
-
115
- raise "output profile #{output_profile} invalid" unless File.exist?(output_profile_file)
116
-
117
- return '' if image.profile.nil?
118
-
119
- # if the input color profile exists, contract paths to the profile and setup the command
120
-
121
- input_profile = profile.gsub(/[^[:alnum:]]/, '') # remove all non alpha-numeric characters, so we can get to a filename
122
-
123
- # construct a path to the input profile, which might exist either in the gem itself or in the tmp folder
124
- input_profile_file_gem = File.join(path_to_profiles, "#{input_profile}.icc")
125
- input_profile_file_tmp = File.join(tmp_folder, "#{input_profile}.icc")
126
- input_profile_file = File.exist?(input_profile_file_gem) ? input_profile_file_gem : input_profile_file_tmp
127
-
128
- # if input profile was extracted and does not matches an existing known profile either in the gem or in the tmp folder,
129
- # we'll issue an imagicmagick command to extract the profile to the tmp folder
130
- unless File.exist?(input_profile_file)
131
- input_profile_extract_command = "MAGICK_TEMPORARY_PATH=#{tmp_folder} convert '#{image.path}'[0] #{input_profile_file}" # extract profile from input image
132
- result = `#{input_profile_extract_command} 2>&1`
133
- raise "input profile extraction command failed: #{input_profile_extract_command} with result #{result}" unless $CHILD_STATUS.success?
134
- # if extraction failed or we cannot write the file, throw exception
135
- raise 'input profile is not a known profile and could not be extracted from input file' unless File.exist?(input_profile_file)
136
- end
137
-
138
- "-profile #{input_profile_file} -profile #{output_profile_file}"
139
- end
140
-
141
103
  # Bigtiff needs to be used if size of image exceeds 2^32 bytes.
142
104
  def need_bigtiff?
143
105
  image.image_data_size >= 2**32
144
106
  end
145
107
 
146
108
  # We do this because we need to reliably compress the tiff and KDUcompress doesn’t support arbitrary image types
109
+ # rubocop:disable Metrics/MethodLength
147
110
  def make_tmp_tiff(tmp_folder: nil)
148
111
  tmp_folder ||= Dir.tmpdir
149
112
  raise "tmp_folder #{tmp_folder} does not exists" unless File.exist?(tmp_folder)
@@ -151,35 +114,20 @@ module Assembly
151
114
  # make temp tiff filename
152
115
  tmp_tiff_file = Tempfile.new(['assembly-image', '.tif'], tmp_folder)
153
116
  tmp_path = tmp_tiff_file.path
117
+ vips_image = Vips::Image.new_from_file image.path
154
118
 
155
- options = []
156
-
157
- # Limit the amount of memory ImageMagick is able to use.
158
- options << '-limit memory 1GiB -limit map 1GiB'
159
-
160
- case image.samples_per_pixel
161
- when 3
162
- options << '-type TrueColor'
163
- when 1
164
- options << '-depth 8' # force the production of a grayscale access derivative
165
- options << '-type Grayscale'
166
- end
167
-
168
- options << profile_conversion_switch(image.profile, tmp_folder: tmp_folder)
169
-
170
- # The output in the covnert command needs to be prefixed by the image type. By default ImageMagick
171
- # will assume TIFF: when the file extension is .tif/.tiff. TIFF64: Needs to be forced when image will
172
- # exceed 2^32 bytes in size
173
- tiff_type = need_bigtiff? ? 'TIFF64:' : ''
174
-
175
- tiff_command = "MAGICK_TEMPORARY_PATH=#{tmp_folder} convert -quiet -compress none #{options.join(' ')} '#{image.path}[0]' #{tiff_type}'#{tmp_path}'"
176
- result = `#{tiff_command} 2>&1`
177
- raise "tiff convert command failed: #{tiff_command} with result #{result}" unless $CHILD_STATUS.success?
119
+ vips_image = if vips_image.interpretation.eql?(:cmyk)
120
+ vips_image.icc_transform(SRGB_ICC, input_profile: CMYK_ICC)
121
+ elsif !image.profile.nil?
122
+ vips_image.icc_transform(SRGB_ICC, embedded: true)
123
+ else
124
+ vips_image
125
+ end
178
126
 
127
+ vips_image.tiffsave(tmp_path, bigtiff: need_bigtiff?)
179
128
  tmp_path
180
129
  end
181
130
  # rubocop:enable Metrics/MethodLength
182
- # rubocop:enable Metrics/AbcSize
183
131
  end
184
132
  end
185
133
  end
@@ -3,6 +3,9 @@
3
3
  module Assembly
4
4
  # the path to the gem, used to access profiles stored with the gem
5
5
  PATH_TO_IMAGE_GEM = File.expand_path(File.dirname(__FILE__) + '/..')
6
+ PATH_TO_PROFILES = File.join(Assembly::PATH_TO_IMAGE_GEM, 'profiles')
7
+ SRGB_ICC = File.join(PATH_TO_PROFILES, 'sRGBIEC6196621.icc')
8
+ CMYK_ICC = File.join(PATH_TO_PROFILES, 'cmyk.icc')
6
9
  end
7
10
 
8
11
  require 'assembly-image/image'
data/profiles/cmyk.icc ADDED
Binary file
@@ -9,11 +9,7 @@ RSpec.describe Assembly::Image::Jp2Creator do
9
9
  let(:input_path) { TEST_TIF_INPUT_FILE }
10
10
  let(:creator) { described_class.new(ai, output: TEST_JP2_OUTPUT_FILE) }
11
11
 
12
- after do
13
- # after each test, empty out the input and output test directories
14
- remove_files(TEST_INPUT_DIR)
15
- remove_files(TEST_OUTPUT_DIR)
16
- end
12
+ before { cleanup }
17
13
 
18
14
  context 'when given an LZW compressed RGB tif' do
19
15
  before do
@@ -31,8 +27,8 @@ RSpec.describe Assembly::Image::Jp2Creator do
31
27
  expect(creator.tmp_path).not_to be_nil
32
28
  expect(result.exif.colorspace).to eq 'sRGB'
33
29
  jp2 = Assembly::Image.new(TEST_JP2_OUTPUT_FILE)
34
- expect(jp2.height).to eq 100
35
- expect(jp2.width).to eq 100
30
+ expect(jp2.height).to eq 36
31
+ expect(jp2.width).to eq 43
36
32
  end
37
33
  end
38
34
 
data/spec/image_spec.rb CHANGED
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
+ require 'fileutils'
4
5
 
5
6
  RSpec.describe Assembly::Image do
6
7
  let(:assembly_image) { described_class.new(input_path) }
7
8
  let(:input_path) { TEST_TIF_INPUT_FILE }
9
+ let(:jp2_output_file) { File.join(TEST_OUTPUT_DIR, File.basename(input_path).gsub('.tif', '.jp2')) }
8
10
 
9
- after do
10
- # after each test, empty out the input and output test directories
11
- remove_files(TEST_INPUT_DIR)
12
- remove_files(TEST_OUTPUT_DIR)
13
- end
11
+ before { cleanup }
14
12
 
15
13
  describe '#jp2_filename' do
16
14
  it 'indicates the default jp2 filename' do
17
- expect(assembly_image.jp2_filename).to eq TEST_TIF_INPUT_FILE.gsub('.tif', '.jp2')
15
+ expect(assembly_image.jp2_filename).to eq input_path.gsub('.tif', '.jp2')
18
16
  end
19
17
 
20
18
  context 'with a file with no extension' do
@@ -26,200 +24,247 @@ RSpec.describe Assembly::Image do
26
24
  end
27
25
  end
28
26
 
29
- describe '#dpg_jp2_filename' do
30
- context 'with a dpg tiff file' do
31
- let(:input_path) { TEST_DPG_TIF_INPUT_FILE }
32
-
33
- it 'indicates the default DPG jp2 filename' do
34
- expect(assembly_image.dpg_jp2_filename).to eq TEST_DPG_TIF_INPUT_FILE.gsub('.tif', '.jp2').gsub('_00_', '_05_')
35
- end
36
- end
37
-
38
- context 'with a file with no extension' do
39
- let(:input_path) { '/path/to/a/file_with_no_00_extension' }
40
-
41
- it 'indicates the default jp2 filename' do
42
- expect(assembly_image.dpg_jp2_filename).to eq '/path/to/a/file_with_no_05_extension.jp2'
43
- end
44
- end
45
- end
46
-
47
27
  describe '#create_jp2' do
48
28
  context 'when input path is blank' do
49
29
  let(:input_path) { '' }
50
30
 
51
31
  it 'does not run if no input file is passed in' do
52
- expect { assembly_image.create_jp2 }.to raise_error
32
+ expect { assembly_image.create_jp2 }.to raise_error(RuntimeError)
53
33
  end
54
34
  end
55
35
 
56
36
  context 'when given an uncompressed compressed RGB tif with more than 4GB of image data', skip: 'This test will create a 4GB test image and a 4GB temporary image, so skipping by default.' do
37
+ let(:input_path) { File.join(TEST_INPUT_DIR, 'rgb.tif') }
38
+
57
39
  before do
58
- generate_test_image(TEST_TIF_INPUT_FILE, compress: 'none', width: '37838', height: '37838')
40
+ generate_test_image(input_path, compress: 'none', width: '37838', height: '37838')
59
41
  end
60
42
 
61
43
  it 'creates the jp2 with a temp file' do
62
- expect(File).to exist TEST_TIF_INPUT_FILE
63
- expect(File).not_to exist TEST_JP2_OUTPUT_FILE
64
- result = assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE)
44
+ expect(File).to exist input_path
45
+ expect(File).not_to exist jp2_output_file
46
+ result = assembly_image.create_jp2(output: jp2_output_file)
65
47
  expect(assembly_image.tmp_path).not_to be_nil
66
48
  expect(result).to be_a_kind_of described_class
67
- expect(result.path).to eq TEST_JP2_OUTPUT_FILE
68
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
49
+ expect(result.path).to eq jp2_output_file
50
+ expect(jp2_output_file).to be_a_jp2
69
51
  expect(result.exif.colorspace).to eq 'sRGB'
70
- jp2 = described_class.new(TEST_JP2_OUTPUT_FILE)
71
- expect(jp2.height).to eq 37_838
72
- expect(jp2.width).to eq 37_838
52
+ expect(result.height).to eq 37_838
53
+ expect(result.width).to eq 37_838
73
54
  end
74
55
  end
75
56
 
76
57
  context 'when given an LZW compressed RGB tif with more than 4GB of image data', skip: 'This test will create a 4GB temporary image, so skipping by default.' do
58
+ let(:input_path) { File.join(TEST_INPUT_DIR, 'lzw.tif') }
59
+
77
60
  before do
78
- generate_test_image(TEST_TIF_INPUT_FILE, compress: 'lzw', width: '37838', height: '37838')
61
+ generate_test_image(input_path, compress: 'lzw', width: '37838', height: '37838')
79
62
  end
80
63
 
81
64
  it 'creates the jp2 with a temp file' do
82
- expect(File).to exist TEST_TIF_INPUT_FILE
83
- expect(File).not_to exist TEST_JP2_OUTPUT_FILE
84
- result = assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE)
65
+ expect(File).to exist input_path
66
+ expect(File).not_to exist jp2_output_file
67
+ expect(assembly_image.exif.samplesperpixel).to be 3
68
+ expect(assembly_image.exif.bitspersample).to eql '8 8 8'
69
+ expect(assembly_image).to be_a_valid_image
70
+ expect(assembly_image).to be_jp2abl
71
+ result = assembly_image.create_jp2(output: jp2_output_file)
85
72
  expect(assembly_image.tmp_path).not_to be_nil
86
73
  expect(result).to be_a_kind_of described_class
87
- expect(result.path).to eq TEST_JP2_OUTPUT_FILE
88
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
74
+ expect(result.path).to eq jp2_output_file
75
+ expect(jp2_output_file).to be_a_jp2
89
76
  expect(result.exif.colorspace).to eq 'sRGB'
90
- jp2 = described_class.new(TEST_JP2_OUTPUT_FILE)
91
- expect(jp2.height).to eq 37_838
92
- expect(jp2.width).to eq 37_838
77
+ expect(result.height).to eq 37_838
78
+ expect(result.width).to eq 37_838
93
79
  end
94
80
  end
95
81
 
96
82
  context 'when given a bitonal tif' do
83
+ let(:input_path) { File.join(TEST_INPUT_DIR, 'bitonal.tif') }
84
+
97
85
  before do
98
- # Need to force group4 compression to get ImageMagick to create bitonal tiff
99
- generate_test_image(TEST_TIF_INPUT_FILE, image_type: 'Bilevel', compress: 'group4')
86
+ generate_test_image(input_path, color: 'bin', bands: 1, depth: 1)
100
87
  end
101
88
 
102
- it 'creates grayscale jp2' do
103
- expect(File).to exist TEST_TIF_INPUT_FILE
104
- expect(File).not_to exist TEST_JP2_OUTPUT_FILE
105
- result = assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE)
106
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
89
+ it 'creates valid jp2' do
90
+ expect(File).to exist input_path
91
+ expect(File).not_to exist jp2_output_file
92
+ expect(assembly_image.exif.samplesperpixel).to be 1
93
+ expect(assembly_image.exif.bitspersample).to be 1
94
+ expect(assembly_image).not_to have_color_profile
95
+ result = assembly_image.create_jp2(output: jp2_output_file)
96
+ expect(result).to be_a_kind_of described_class
97
+ expect(result.path).to eq jp2_output_file
98
+ expect(jp2_output_file).to be_a_jp2
107
99
  expect(result.exif.colorspace).to eq 'Grayscale'
108
100
  end
109
101
  end
110
102
 
111
103
  context 'when given a color tif but bitonal image data (1 channels and 1 bits per pixel)' do
104
+ let(:input_path) { File.join(TEST_INPUT_DIR, 'color.tif') }
105
+
112
106
  before do
113
- generate_test_image(TEST_TIF_INPUT_FILE, color: 'Bilevel', image_type: 'TrueColor', profile: '')
107
+ generate_test_image(input_path, color: 'bin', bands: 3)
114
108
  end
115
109
 
116
110
  it 'creates color jp2' do
117
- expect(File).to exist TEST_TIF_INPUT_FILE
118
- expect(File).not_to exist TEST_JP2_OUTPUT_FILE
111
+ expect(File).to exist input_path
112
+ expect(File).not_to exist jp2_output_file
119
113
  expect(assembly_image).not_to have_color_profile
120
- result = assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE)
121
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
114
+ expect(assembly_image.exif.samplesperpixel).to be 3
115
+ expect(assembly_image.exif.bitspersample).to eql '8 8 8'
116
+ expect(assembly_image).to be_a_valid_image
117
+ expect(assembly_image).to be_jp2able
118
+ result = assembly_image.create_jp2(output: jp2_output_file)
119
+ expect(result).to be_a_kind_of described_class
120
+ expect(result.path).to eq jp2_output_file
121
+ expect(jp2_output_file).to be_a_jp2
122
122
  expect(result.exif.colorspace).to eq 'sRGB'
123
123
  end
124
124
  end
125
125
 
126
126
  context 'when given a graycale tif but with bitonal image data (1 channel and 1 bits per pixel)' do
127
+ let(:input_path) { File.join(TEST_INPUT_DIR, 'gray.tif') }
128
+
127
129
  before do
128
- generate_test_image(TEST_TIF_INPUT_FILE, color: 'Bilevel', image_type: 'Grayscale', profile: '')
130
+ generate_test_image(input_path, color: 'grey', bands: 1)
129
131
  end
130
132
 
131
133
  it 'creates grayscale jp2' do
132
- expect(File).to exist TEST_TIF_INPUT_FILE
133
- expect(File).not_to exist TEST_JP2_OUTPUT_FILE
134
+ expect(File).to exist input_path
135
+ expect(File).not_to exist jp2_output_file
134
136
  expect(assembly_image).not_to have_color_profile
135
- result = assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE)
136
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
137
+ expect(assembly_image.exif.samplesperpixel).to be 1
138
+ expect(assembly_image.exif.bitspersample).to be 8
139
+ expect(assembly_image).to be_a_valid_image
140
+ expect(assembly_image).to be_jp2able
141
+ result = assembly_image.create_jp2(output: jp2_output_file)
142
+ expect(jp2_output_file).to be_a_jp2
143
+ expect(result).to be_a_kind_of described_class
144
+ expect(result.path).to eq jp2_output_file
137
145
  expect(result.exif.colorspace).to eq 'Grayscale'
138
146
  end
139
147
  end
140
148
 
141
149
  context 'when given a color tif but with greyscale image data (1 channel and 8 bits per pixel)' do
150
+ let(:input_path) { File.join(TEST_INPUT_DIR, 'color_gray.tif') }
151
+
142
152
  before do
143
- generate_test_image(TEST_TIF_INPUT_FILE, color: 'Grayscale', image_type: 'TrueColor', profile: '')
153
+ generate_test_image(input_path, color: 'grey')
144
154
  end
145
155
 
146
156
  it 'creates color jp2' do
147
- expect(File).to exist TEST_TIF_INPUT_FILE
148
- expect(File).not_to exist TEST_JP2_OUTPUT_FILE
157
+ expect(File).to exist input_path
158
+ expect(File).not_to exist jp2_output_file
159
+ expect(assembly_image.exif.samplesperpixel).to be 3
160
+ expect(assembly_image.exif.bitspersample).to eql '8 8 8'
161
+ expect(assembly_image).to be_a_valid_image
162
+ expect(assembly_image).to be_jp2able
149
163
  expect(assembly_image).not_to have_color_profile
150
- result = assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE)
151
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
164
+ result = assembly_image.create_jp2(output: jp2_output_file)
165
+ expect(result).to be_a_kind_of described_class
166
+ expect(result.path).to eq jp2_output_file
167
+ expect(jp2_output_file).to be_a_jp2
152
168
  expect(result.exif.colorspace).to eq 'sRGB'
153
169
  end
154
170
  end
155
171
 
172
+ context 'when given a cmyk tif' do
173
+ let(:input_path) { File.join(TEST_INPUT_DIR, 'cmky.tif') }
174
+
175
+ before do
176
+ generate_test_image(input_path, color: 'cmyk', cg_type: 'cmyk', profile: 'cmyk', bands: 4)
177
+ end
178
+
179
+ it 'creates an srgb jp2', skip: 'Need to verify the color space is correct in jp2' do
180
+ expect(File).to exist input_path
181
+ expect(File).not_to exist jp2_output_file
182
+ expect(assembly_image.exif.samplesperpixel).to be 4
183
+ expect(assembly_image.exif.bitspersample).to eql '8 8 8 8'
184
+ expect(assembly_image).to be_a_valid_image
185
+ expect(assembly_image).to be_jp2able
186
+ expect(assembly_image).to have_color_profile
187
+ result = assembly_image.create_jp2(output: jp2_output_file)
188
+ expect(result).to be_a_kind_of described_class
189
+ expect(result.path).to eq jp2_output_file
190
+ expect(jp2_output_file).to be_a_jp2
191
+ # note, we verify the CMYK has been converted to an SRGB JP2 correctly by using ruby-vips instead of exif, since exif does not correctly
192
+ # identify the color space...note: this line current does not work in circleci, potentially due to libvips version differences
193
+ expect(Vips::Image.new_from_file(jp2_output_file).get_value('interpretation')).to eq :srgb
194
+ end
195
+ end
196
+
156
197
  context 'when the source image has no profile' do
198
+ let(:input_path) { File.join(TEST_INPUT_DIR, 'no_profile.tif') }
199
+
157
200
  before do
158
- generate_test_image(TEST_TIF_INPUT_FILE, profile: '') # generate a test input with no profile
201
+ generate_test_image(input_path)
159
202
  end
160
203
 
161
204
  it 'creates a jp2' do
162
- expect(File).to exist TEST_TIF_INPUT_FILE
163
- expect(File).not_to exist TEST_JP2_OUTPUT_FILE
205
+ expect(File).to exist input_path
206
+ expect(File).not_to exist jp2_output_file
207
+ expect(assembly_image.exif.samplesperpixel).to be 3
208
+ expect(assembly_image.exif.bitspersample).to eql '8 8 8'
164
209
  expect(assembly_image).not_to have_color_profile
165
210
  expect(assembly_image).to be_a_valid_image
166
211
  expect(assembly_image).to be_jp2able
167
- assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE)
168
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
212
+ assembly_image.create_jp2(output: jp2_output_file)
213
+ expect(jp2_output_file).to be_a_jp2
169
214
  end
170
215
  end
171
216
 
172
217
  context "when the output file exists and you don't allow overwriting" do
173
218
  before do
174
- generate_test_image(TEST_TIF_INPUT_FILE)
175
- generate_test_image(TEST_JP2_OUTPUT_FILE)
219
+ generate_test_image(input_path)
220
+ FileUtils.touch(jp2_output_file) # just need a file with this name, don't care what
176
221
  end
177
222
 
178
223
  it 'does not run' do
179
- expect(File).to exist TEST_TIF_INPUT_FILE
180
- expect(File).to exist TEST_JP2_OUTPUT_FILE
181
- expect { assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE) }.to raise_error(SecurityError)
224
+ expect(File).to exist input_path
225
+ expect(File).to exist jp2_output_file
226
+ expect { assembly_image.create_jp2(output: jp2_output_file) }.to raise_error(SecurityError)
182
227
  end
183
228
  end
184
229
 
185
230
  context 'when given a test tiff' do
186
231
  before do
187
- generate_test_image(TEST_TIF_INPUT_FILE)
232
+ generate_test_image(input_path)
188
233
  end
189
234
 
190
235
  it 'gets the correct image height and width' do
191
- expect(assembly_image.height).to eq 100
192
- expect(assembly_image.width).to eq 100
236
+ expect(assembly_image.height).to eq 36
237
+ expect(assembly_image.width).to eq 43
193
238
  end
194
239
  end
195
240
 
196
241
  context 'when the input file is a jp2' do
197
242
  before do
198
- generate_test_image(TEST_TIF_INPUT_FILE, profile: '') # generate a test input with no profile
243
+ generate_test_image(input_path)
199
244
  end
200
245
 
201
246
  it 'does not run' do
202
- expect(File).to exist TEST_TIF_INPUT_FILE
203
- expect(File).not_to exist TEST_JP2_OUTPUT_FILE
247
+ expect(File).to exist input_path
248
+ expect(File).not_to exist jp2_output_file
204
249
  expect(assembly_image).not_to have_color_profile
205
250
  expect(assembly_image).to be_a_valid_image
206
251
  expect(assembly_image).to be_jp2able
207
- assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE)
208
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
209
- jp2_file = described_class.new(TEST_JP2_OUTPUT_FILE)
252
+ assembly_image.create_jp2(output: jp2_output_file)
253
+ expect(jp2_output_file).to be_a_jp2
254
+ jp2_file = described_class.new(jp2_output_file)
210
255
  expect(jp2_file).to be_valid_image
211
256
  expect(jp2_file).not_to be_jp2able
212
- expect { jp2_file.create_jp2 }.to raise_error
257
+ expect { jp2_file.create_jp2 }.to raise_error(RuntimeError)
213
258
  end
214
259
  end
215
260
 
216
261
  context 'when you specify a bogus output profile' do
217
262
  before do
218
- generate_test_image(TEST_TIF_INPUT_FILE)
263
+ generate_test_image(input_path)
219
264
  end
220
265
 
221
266
  it 'runs, because this is not currently an option' do
222
- expect(File).to exist TEST_TIF_INPUT_FILE
267
+ expect(File).to exist input_path
223
268
  result = assembly_image.create_jp2(output_profile: 'bogusness')
224
269
  expect(result).to be_a_kind_of described_class
225
270
  expect(result.path).to eq TEST_JP2_INPUT_FILE
@@ -239,17 +284,17 @@ RSpec.describe Assembly::Image do
239
284
  bogus_folder = '/crapsticks'
240
285
  expect(File).to exist TEST_JPEG_INPUT_FILE
241
286
  expect(File).not_to exist bogus_folder
242
- expect { assembly_image.create_jp2(tmp_folder: bogus_folder) }.to raise_error
287
+ expect { assembly_image.create_jp2(tmp_folder: bogus_folder) }.to raise_error(RuntimeError)
243
288
  end
244
289
  end
245
290
 
246
291
  context 'when no output file is specified' do
247
292
  before do
248
- generate_test_image(TEST_TIF_INPUT_FILE)
293
+ generate_test_image(input_path)
249
294
  end
250
295
 
251
296
  it 'creates a jp2 of the same filename and in the same location as the input and cleans up the tmp file' do
252
- expect(File).to exist TEST_TIF_INPUT_FILE
297
+ expect(File).to exist input_path
253
298
  expect(File.exist?(TEST_JP2_INPUT_FILE)).to be false
254
299
  result = assembly_image.create_jp2
255
300
  expect(result).to be_a_kind_of described_class
@@ -261,17 +306,17 @@ RSpec.describe Assembly::Image do
261
306
 
262
307
  context 'when the output file exists and you allow overwriting' do
263
308
  before do
264
- generate_test_image(TEST_TIF_INPUT_FILE)
265
- generate_test_image(TEST_JP2_OUTPUT_FILE)
309
+ generate_test_image(input_path)
310
+ FileUtils.touch(jp2_output_file) # just need a file with this name, don't care what
266
311
  end
267
312
 
268
313
  it 'recreates jp2' do
269
- expect(File).to exist TEST_TIF_INPUT_FILE
270
- expect(File).to exist TEST_JP2_OUTPUT_FILE
271
- result = assembly_image.create_jp2(output: TEST_JP2_OUTPUT_FILE, overwrite: true)
314
+ expect(File).to exist input_path
315
+ expect(File).to exist jp2_output_file
316
+ result = assembly_image.create_jp2(output: jp2_output_file, overwrite: true)
272
317
  expect(result).to be_a_kind_of described_class
273
- expect(result.path).to eq TEST_JP2_OUTPUT_FILE
274
- expect(TEST_JP2_OUTPUT_FILE).to be_a_jp2
318
+ expect(result.path).to eq jp2_output_file
319
+ expect(jp2_output_file).to be_a_jp2
275
320
  expect(result.exif.colorspace).to eq 'sRGB'
276
321
  end
277
322
  end
data/spec/images_spec.rb CHANGED
@@ -3,45 +3,45 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Assembly::Images do
6
- after do
7
- # after each test, empty out the input and output test directories
8
- remove_files(TEST_INPUT_DIR)
9
- remove_files(TEST_OUTPUT_DIR)
6
+ before { cleanup }
7
+
8
+ describe '#batch_generate_jp2' do
9
+ it 'does not run if no input folder is passed in' do
10
+ expect{ described_class.batch_generate_jp2('') }.to raise_error(RuntimeError)
11
+ end
12
+
13
+ it 'does not run if a non-existent input folder is passed in' do
14
+ expect{ described_class.batch_generate_jp2('/junk/path') }.to raise_error(RuntimeError)
15
+ end
16
+
17
+ it 'runs and batch produces jp2s from input tiffs' do
18
+ ['test1', 'test2', 'test3'].each { |image| generate_test_image(File.join(TEST_INPUT_DIR, "#{image}.tif"), profile: 'AdobeRGB1998') }
19
+ described_class.batch_generate_jp2(TEST_INPUT_DIR, output: TEST_OUTPUT_DIR)
20
+ expect(File.directory?(TEST_OUTPUT_DIR)).to be true
21
+ ['test1', 'test2', 'test3'].each { |image| expect(File.join(TEST_OUTPUT_DIR, "#{image}.jp2")).to be_a_jp2 }
22
+ end
10
23
  end
11
24
 
12
- it 'does not run if no input folder is passed in' do
13
- expect{ described_class.batch_generate_jp2('') }.to raise_error
14
- end
15
-
16
- it 'does not run if a non-existent input folder is passed in' do
17
- expect{ described_class.batch_generate_jp2('/junk/path') }.to raise_error
18
- end
19
-
20
- it 'runs and batch produces jp2s from input tiffs' do
21
- ['test1', 'test2', 'test3'].each { |image| generate_test_image(File.join(TEST_INPUT_DIR, "#{image}.tif")) }
22
- described_class.batch_generate_jp2(TEST_INPUT_DIR, output: TEST_OUTPUT_DIR)
23
- expect(File.directory?(TEST_OUTPUT_DIR)).to be true
24
- ['test1', 'test2', 'test3'].each { |image| expect(File.join(TEST_OUTPUT_DIR, "#{image}.jp2")).to be_a_jp2 }
25
- end
26
-
27
- it 'runs and batch add color profile descriptions input tiffs with no color profile descriptions' do
28
- ['test1', 'test2', 'test3'].each { |image| generate_test_image(File.join(TEST_INPUT_DIR, "#{image}.tif"), profile: '') }
29
- ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to be_nil }
30
- described_class.batch_add_exif_profile_descr(TEST_INPUT_DIR, 'Adobe RGB 1998')
31
- ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'Adobe RGB (1998)' }
32
- end
33
-
34
- it 'runs and batch add color profile descriptions input tiffs, forcing over existing color profile descriptions' do
35
- ['test1', 'test2', 'test3'].each { |image| generate_test_image(File.join(TEST_INPUT_DIR, "#{image}.tif")) }
36
- ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'sRGB IEC61966-2.1' }
37
- described_class.batch_add_exif_profile_descr(TEST_INPUT_DIR, 'Adobe RGB 1998', force: true) # force overwrite
38
- ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'Adobe RGB (1998)' }
39
- end
40
-
41
- it 'runs and batch add color profile descriptions input tiffs, not overwriting existing color profile descriptions' do
42
- ['test1', 'test2', 'test3'].each { |image| generate_test_image(File.join(TEST_INPUT_DIR, "#{image}.tif")) }
43
- ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'sRGB IEC61966-2.1' }
44
- described_class.batch_add_exif_profile_descr(TEST_INPUT_DIR, 'Adobe RGB 1998') # do not force overwrite
45
- ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'sRGB IEC61966-2.1' }
25
+ describe '#batch_add_exif_profile_descr' do
26
+ it 'runs and batch adds color profile descriptions to input tiffs that had no color profile descriptions' do
27
+ ['test1', 'test2', 'test3'].each { |image| generate_test_image(File.join(TEST_INPUT_DIR, "#{image}.tif")) }
28
+ ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to be_nil }
29
+ described_class.batch_add_exif_profile_descr(TEST_INPUT_DIR, 'Adobe RGB 1998')
30
+ ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'Adobe RGB (1998)' }
31
+ end
32
+
33
+ it 'runs and batch adds color profile descriptions to input tiffs, forcing over existing color profile descriptions' do
34
+ ['test1', 'test2', 'test3'].each { |image| generate_test_image(File.join(TEST_INPUT_DIR, "#{image}.tif"), profile: 'sRGBIEC6196621') }
35
+ ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'sRGB IEC61966-2.1' }
36
+ described_class.batch_add_exif_profile_descr(TEST_INPUT_DIR, 'Adobe RGB 1998', force: true) # force overwrite
37
+ ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'Adobe RGB (1998)' }
38
+ end
39
+
40
+ it 'runs and batch adds color profile descriptions to input tiffs, not overwriting existing color profile descriptions' do
41
+ ['test1', 'test2', 'test3'].each { |image| generate_test_image(File.join(TEST_INPUT_DIR, "#{image}.tif"), profile: 'sRGBIEC6196621') }
42
+ ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'sRGB IEC61966-2.1' }
43
+ described_class.batch_add_exif_profile_descr(TEST_INPUT_DIR, 'Adobe RGB 1998') # do not force overwrite
44
+ ['test1', 'test2', 'test3'].each { |image| expect(Assembly::Image.new(File.join(TEST_INPUT_DIR, "#{image}.tif")).exif.profiledescription).to eq 'sRGB IEC61966-2.1' }
45
+ end
46
46
  end
47
47
  end
data/spec/spec_helper.rb CHANGED
@@ -7,14 +7,17 @@ end
7
7
 
8
8
  bootfile = File.expand_path(File.dirname(__FILE__) + '/../config/boot')
9
9
  require bootfile
10
+ require 'ruby-vips'
11
+ require 'pry-byebug'
10
12
 
11
13
  TEST_INPUT_DIR = File.join(Assembly::PATH_TO_IMAGE_GEM, 'spec', 'test_data', 'input')
12
14
  TEST_OUTPUT_DIR = File.join(Assembly::PATH_TO_IMAGE_GEM, 'spec', 'test_data', 'output')
13
15
  TEST_TIF_INPUT_FILE = File.join(TEST_INPUT_DIR, 'test.tif')
14
- TEST_DPG_TIF_INPUT_FILE = File.join(TEST_INPUT_DIR, 'oo000oo0001_00_01.tif')
15
16
  TEST_JPEG_INPUT_FILE = File.join(TEST_INPUT_DIR, 'test.jpg')
16
17
  TEST_JP2_INPUT_FILE = File.join(TEST_INPUT_DIR, 'test.jp2')
17
18
  TEST_JP2_OUTPUT_FILE = File.join(TEST_OUTPUT_DIR, 'test.jp2')
19
+ TEST_PROFILE_DIR = File.join(Assembly::PATH_TO_IMAGE_GEM, 'profiles')
20
+ TEST_DATA_DIR = File.join(Assembly::PATH_TO_IMAGE_GEM, 'spec', 'test_data')
18
21
  TEST_DRUID = 'nx288wh8889'
19
22
 
20
23
  RSpec.configure do |config|
@@ -101,31 +104,131 @@ RSpec.configure do |config|
101
104
  Kernel.srand config.seed
102
105
  end
103
106
 
107
+ # rubocop:disable Metrics/MethodLength
108
+ # Color values for 30-patch ColorGauge color target.
109
+ def color_gauge_values(type = 'adobeRGB')
110
+ # rubocop:disable Layout/SpaceInsideArrayLiteralBrackets
111
+ # rubocop:disable Layout/ExtraSpacing
112
+ adobe_rgb = [
113
+ [109, 83, 71], [187, 146, 129], [101, 120, 151], [ 97, 108, 68], [130, 128, 172],
114
+ [130, 187, 171], [ 64, 134, 165], [241, 242, 237], [231, 232, 229], [216, 217, 215],
115
+ [203, 204, 203], [202, 125, 55], [172, 87, 147], [174, 176, 175], [148, 150, 149],
116
+ [116, 119, 118], [ 91, 91, 92], [ 78, 92, 165], [227, 198, 55], [ 68, 70, 69],
117
+ [ 48, 48, 48], [ 32, 32, 32], [ 23, 23, 23], [175, 85, 97], [157, 60, 61],
118
+ [100, 148, 80], [ 53, 67, 141], [213, 160, 56], [167, 187, 77], [ 86, 61, 100]
119
+ ]
120
+
121
+ srgb = [
122
+ [118, 82, 69], [202, 147, 129], [ 92, 121, 154], [ 92, 109, 64], [132, 129, 175],
123
+ [ 96, 188, 172], [ 0, 135, 168], [241, 242, 237], [231, 232, 229], [217, 218, 216],
124
+ [204, 205, 204], [225, 126, 46], [196, 86, 150], [175, 178, 177], [148, 151, 150],
125
+ [116, 120, 119], [ 91, 91, 92], [ 70, 92, 169], [238, 199, 27], [ 65, 68, 67],
126
+ [ 44, 44, 44], [ 26, 26, 26], [ 16, 16, 16], [200, 84, 97], [181, 57, 58],
127
+ [ 68, 149, 74], [ 42, 65, 145], [231, 161, 41], [160, 188, 65], [ 94, 58, 101]
128
+ ]
129
+
130
+ cmyk = [
131
+ [120, 154, 169, 84], [ 69, 110, 120, 5], [169, 125, 64, 8], [154, 105, 207, 64],
132
+ [138, 125, 31, 0], [128, 26, 95, 0], [195, 95, 61, 3], [ 10, 5, 13, 0],
133
+ [ 20, 13, 18, 0], [ 36, 26, 31, 0], [ 51, 38, 41, 0], [ 46, 143, 236, 8],
134
+ [ 90, 202, 31, 0], [ 84, 64, 69, 0], [115, 90, 95, 0], [143, 115, 120, 28],
135
+ [161, 141, 136, 69], [205, 182, 8, 0], [ 33, 46, 238, 0], [172, 151, 151, 110],
136
+ [179, 164, 161, 156], [184, 169, 166, 189], [187, 172, 166, 205], [ 69, 197, 131, 20],
137
+ [ 69, 220, 189, 51], [161, 59, 223, 15], [241, 223, 28, 5], [ 44, 95, 238, 3],
138
+ [100, 31, 228, 0], [184, 210, 90, 56]
139
+ ]
140
+ # rubocop:enable Layout/SpaceInsideArrayLiteralBrackets
141
+ # rubocop:enable Layout/ExtraSpacing
142
+ case type
143
+ when 'adobe_rgb'
144
+ adobe_rgb
145
+ when 'srgb'
146
+ srgb
147
+ when 'cmyk'
148
+ cmyk
149
+ else
150
+ raise 'Unknown color_gauge_values type.'
151
+ end
152
+ end
153
+ # rubocop:enable Metrics/MethodLength
154
+
104
155
  # generate a sample image file with a specified profile
105
156
  # rubocop:disable Metrics/AbcSize
106
157
  # rubocop:disable Metrics/CyclomaticComplexity
107
158
  # rubocop:disable Metrics/MethodLength
108
159
  # rubocop:disable Metrics/PerceivedComplexity
109
160
  def generate_test_image(file, params = {})
110
- width = params[:width] || '100'
111
- height = params[:height] || '100'
112
- color = params[:color] || 'TrueColor'
113
- profile = params[:profile] || 'sRGBIEC6196621'
114
- image_type = params[:image_type]
115
- create_command = "convert rose: -scale #{width}x#{height}\! -type #{color} "
116
- create_command += ' -profile ' + File.join(Assembly::PATH_TO_IMAGE_GEM, 'profiles', profile + '.icc') + ' ' unless profile == ''
117
- create_command += " -type #{image_type} " if image_type
118
- create_command += ' -compress lzw ' if params[:compress]
119
- create_command += file
120
- create_command += ' 2>&1'
121
- output = `#{create_command}`
122
- raise "Failed to create test image #{file} (#{params}): \n#{output}" unless $CHILD_STATUS.success?
161
+ # Set default size for sample test image.
162
+ line_size = 1
163
+ box_size = 6
164
+ width = (box_size * 6) + (line_size * 7)
165
+ height = (box_size * 5) + (line_size * 6)
166
+
167
+ # Set parameters for image creation options.
168
+ image_type = params[:image_type] || File.extname(file)
169
+ bands = params[:bands] || 3
170
+ color = params[:color] || 'rgb'
171
+ depth = params[:depth] || 8
172
+ cg_type = params[:cg_type] || 'adobe_rgb'
173
+ compression = params[:compression]
174
+ profile = params[:profile]
175
+
176
+ temp_array = color_gauge_values(cg_type)
177
+ temp_image = Vips::Image.black(width, height, bands: temp_array.first.size)
178
+ (0..4).each do |i|
179
+ b = (box_size * i) + (line_size * (i + 1))
180
+ # d = b + box_size - line_size
181
+ (0...6).each do |j|
182
+ a = (box_size * j) + (line_size * (j + 1))
183
+ # c = a + box_size - line_size
184
+ colors = temp_array.shift
185
+ temp_image = temp_image.draw_rect(colors, a, b, box_size, box_size, fill: true)
186
+ end
187
+ end
188
+
189
+ temp_image = color.eql?('cmyk') ? temp_image.copy(interpretation: :cmyk) : temp_image.copy(interpretation: :srgb)
190
+
191
+ temp_image = if color.eql?('grey') && bands == 3
192
+ mean = temp_image.bandmean
193
+ Vips::Image.bandjoin([mean, mean, mean])
194
+ elsif color.eql?('grey') && bands == 1
195
+ temp_image.bandmean
196
+ elsif color.eql?('bin') && bands == 3
197
+ mean = temp_image.bandmean < 128
198
+ Vips::Image.bandjoin([mean, mean, mean])
199
+ elsif color.eql?('bin') && bands == 1
200
+ temp_image.bandmean < 128
201
+ else
202
+ temp_image
203
+ end
204
+
205
+ options = {}
206
+ unless profile.nil?
207
+ profile_file = File.join(TEST_PROFILE_DIR, profile + '.icc')
208
+ options.merge!(profile: profile_file)
209
+ end
210
+
211
+ case image_type
212
+ when '.tiff', '.tif'
213
+ options.merge!(compression: compression) unless compression.nil?
214
+ options.merge!(squash: true) if depth.eql?(1)
215
+ temp_image.tiffsave(file, **options)
216
+ when '.jpeg', '.jpg'
217
+ temp_image.jpegsave(file, **options)
218
+ else
219
+ raise "unknown type: #{image_type}"
220
+ end
123
221
  end
124
222
  # rubocop:enable Metrics/AbcSize
125
223
  # rubocop:enable Metrics/CyclomaticComplexity
126
224
  # rubocop:enable Metrics/MethodLength
127
225
  # rubocop:enable Metrics/PerceivedComplexity
128
226
 
227
+ def cleanup
228
+ remove_files(TEST_INPUT_DIR)
229
+ remove_files(TEST_OUTPUT_DIR)
230
+ end
231
+
129
232
  def remove_files(dir)
130
233
  Dir.foreach(dir) do |f|
131
234
  fn = File.join(dir, f)
Binary file
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assembly-image
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Mangiafico
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2022-06-15 00:00:00.000000000 Z
14
+ date: 2022-06-27 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: assembly-objectfile
@@ -47,6 +47,34 @@ dependencies:
47
47
  - - "<"
48
48
  - !ruby/object:Gem::Version
49
49
  version: '3'
50
+ - !ruby/object:Gem::Dependency
51
+ name: ruby-vips
52
+ requirement: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '2.0'
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '2.0'
64
+ - !ruby/object:Gem::Dependency
65
+ name: pry-byebug
66
+ requirement: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
50
78
  - !ruby/object:Gem::Dependency
51
79
  name: rake
52
80
  requirement: !ruby/object:Gem::Requirement
@@ -152,7 +180,6 @@ files:
152
180
  - Rakefile
153
181
  - assembly-image.gemspec
154
182
  - bin/console
155
- - bin/run_all_tests
156
183
  - config/boot.rb
157
184
  - lib/assembly-image.rb
158
185
  - lib/assembly-image/image.rb
@@ -160,11 +187,20 @@ files:
160
187
  - lib/assembly-image/jp2_creator.rb
161
188
  - profiles/AdobeRGB1998.icc
162
189
  - profiles/DotGain20.icc
190
+ - profiles/cmyk.icc
163
191
  - profiles/sRGBIEC6196621.icc
164
192
  - spec/assembly/image/jp2_creator_spec.rb
165
193
  - spec/image_spec.rb
166
194
  - spec/images_spec.rb
167
195
  - spec/spec_helper.rb
196
+ - spec/test_data/color_cmyk_tagged.tif
197
+ - spec/test_data/color_cmyk_untagged.tif
198
+ - spec/test_data/color_rgb_adobergb1998_lzw.tif
199
+ - spec/test_data/color_rgb_srgb.jpg
200
+ - spec/test_data/color_rgb_srgb.tif
201
+ - spec/test_data/color_rgb_srgb_rot90cw.tif
202
+ - spec/test_data/color_rgb_untagged.tif
203
+ - spec/test_data/gray_gray_untagged.tif
168
204
  - spec/test_data/input/.empty
169
205
  - spec/test_data/output/.empty
170
206
  homepage: ''
@@ -191,10 +227,4 @@ signing_key:
191
227
  specification_version: 4
192
228
  summary: Ruby immplementation of image services needed to prepare objects to be accessioned
193
229
  in SULAIR digital library
194
- test_files:
195
- - spec/assembly/image/jp2_creator_spec.rb
196
- - spec/image_spec.rb
197
- - spec/images_spec.rb
198
- - spec/spec_helper.rb
199
- - spec/test_data/input/.empty
200
- - spec/test_data/output/.empty
230
+ test_files: []
data/bin/run_all_tests DELETED
@@ -1,3 +0,0 @@
1
- #! /bin/bash
2
-
3
- bundle exec rspec spec