assembly-image 1.9.0 → 2.0.0

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 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