assembly-image 2.1.4 → 2.3.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: 5aa835ae3183f8de14d0e677ded80d0ee5ffd813b2150e9fc4665245964dbab1
4
- data.tar.gz: 639cad1edcc8abf76c26598bfd4949085a2fdea49f1acf5802a5a75f6b887c68
3
+ metadata.gz: fa79dbf8616f62f2ec19a862730b26e6c35151effcfa1a928babed128e7792fd
4
+ data.tar.gz: 6312f2cca6e2d431e6b20743d7514994a1244c38c253ba9cfe736efedc5d2b3a
5
5
  SHA512:
6
- metadata.gz: dcc568cc992f74a80e69b6f6bae2eb56a5611a22e848371ce669634037181aae4f735ca2823e35b64037609a4c3b3f473d556fd3830a4d5a33c13d24b6536a59
7
- data.tar.gz: f6cdae8947215307048dc439ef76308cb12b3523ed4722258c0d156bdee189c10c5fab6aee444ab09aeb3b3bff59a3ee4c00a3b5cf51619293a9a1ac8b214f7e
6
+ metadata.gz: adcd39ced78216223ebde02426945048c2f6b28919dee87924736c523d916af196f95879b28518eb80844dbe550fd6f6d10cc83506b7294f58a11d5159e9ed22
7
+ data.tar.gz: fd104a2b3202efaf3ba7bed7cfbbc816d668b21f27555fed88ff47019d24da745011b66393db4f708fb5d280d3ea9fb421ec8dc2ae483bda616ab56087d70f7d
data/.circleci/config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  version: 2.1
2
2
  orbs:
3
- ruby-rails: sul-dlss/ruby-rails@4.6.0
3
+ ruby-rails: sul-dlss/ruby-rails@4.12.0
4
4
  workflows:
5
5
  build:
6
6
  jobs:
data/.rubocop.yml CHANGED
@@ -26,7 +26,7 @@ Naming/FileName:
26
26
  Exclude:
27
27
  - lib/assembly-image.rb
28
28
 
29
- Naming/PredicateName:
29
+ Naming/PredicatePrefix:
30
30
  ForbiddenPrefixes:
31
31
  - is_
32
32
 
@@ -184,7 +184,7 @@ Lint/ConstantOverwrittenInRescue: # new in 1.31
184
184
  Lint/NonAtomicFileOperation: # new in 1.31
185
185
  Enabled: true
186
186
 
187
- Capybara/SpecificMatcher: # new in 2.12
187
+ Capybara/RSpec/SpecificMatcher: # new in 2.12
188
188
  Enabled: true
189
189
  RSpecRails/HaveHttpStatus: # new in 2.12
190
190
  Enabled: false
@@ -243,9 +243,9 @@ Style/RedundantRegexpConstructor: # new in 1.52
243
243
  Enabled: true
244
244
  Style/RedundantStringEscape: # new in 1.37
245
245
  Enabled: true
246
- Capybara/MatchStyle: # new in 2.17
246
+ Capybara/RSpec/MatchStyle: # new in 2.17
247
247
  Enabled: true
248
- Capybara/NegationMatcher: # new in 2.14
248
+ Capybara/RSpec/NegationMatcher: # new in 2.14
249
249
  Enabled: true
250
250
  Capybara/SpecificActions: # new in 2.14
251
251
  Enabled: true
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- assembly-image (2.1.4)
4
+ assembly-image (2.3.0)
5
5
  activesupport (> 6.1)
6
6
  assembly-objectfile (>= 1.6.4)
7
7
  ruby-vips (>= 2.0)
@@ -9,7 +9,7 @@ PATH
9
9
  GEM
10
10
  remote: http://rubygems.org/
11
11
  specs:
12
- activesupport (8.1.2)
12
+ activesupport (8.1.3)
13
13
  base64
14
14
  bigdecimal
15
15
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -28,8 +28,8 @@ GEM
28
28
  mini_exiftool
29
29
  ast (2.4.3)
30
30
  base64 (0.3.0)
31
- bigdecimal (4.0.1)
32
- concurrent-ruby (1.3.6)
31
+ bigdecimal (4.1.2)
32
+ concurrent-ruby (1.3.7)
33
33
  connection_pool (3.0.2)
34
34
  date (3.5.1)
35
35
  debug (1.11.1)
@@ -38,51 +38,53 @@ GEM
38
38
  diff-lcs (1.6.2)
39
39
  docile (1.4.1)
40
40
  drb (2.2.3)
41
- erb (6.0.1)
42
- ffi (1.17.3-arm64-darwin)
43
- ffi (1.17.3-x86_64-darwin)
44
- ffi (1.17.3-x86_64-linux-gnu)
45
- i18n (1.14.8)
41
+ erb (6.0.4)
42
+ ffi (1.17.4-arm64-darwin)
43
+ ffi (1.17.4-x86_64-darwin)
44
+ ffi (1.17.4-x86_64-linux-gnu)
45
+ i18n (1.15.2)
46
46
  concurrent-ruby (~> 1.0)
47
47
  io-console (0.8.2)
48
- irb (1.16.0)
48
+ irb (1.18.0)
49
49
  pp (>= 0.6.0)
50
+ prism (>= 1.3.0)
50
51
  rdoc (>= 4.0.0)
51
52
  reline (>= 0.4.2)
52
- json (2.18.0)
53
+ json (2.19.9)
53
54
  language_server-protocol (3.17.0.5)
54
55
  lint_roller (1.1.0)
55
56
  logger (1.7.0)
56
57
  mime-types (3.7.0)
57
58
  logger
58
59
  mime-types-data (~> 3.2025, >= 3.2025.0507)
59
- mime-types-data (3.2025.0924)
60
+ mime-types-data (3.2026.0414)
60
61
  mini_exiftool (2.14.0)
61
62
  ostruct (>= 0.6.0)
62
63
  pstore (>= 0.1.3)
63
- minitest (6.0.1)
64
+ minitest (6.0.6)
65
+ drb (~> 2.0)
64
66
  prism (~> 1.5)
65
67
  ostruct (0.6.3)
66
- parallel (1.27.0)
67
- parser (3.3.10.0)
68
+ parallel (2.1.0)
69
+ parser (3.3.11.1)
68
70
  ast (~> 2.4.1)
69
71
  racc
70
- pp (0.6.3)
72
+ pp (0.6.4)
71
73
  prettyprint
72
74
  prettyprint (0.2.0)
73
- prism (1.7.0)
74
- pstore (0.2.0)
75
- psych (5.3.1)
75
+ prism (1.9.0)
76
+ pstore (0.2.1)
77
+ psych (5.4.0)
76
78
  date
77
79
  stringio
78
80
  racc (1.8.1)
79
81
  rainbow (3.1.1)
80
- rake (13.3.1)
81
- rdoc (7.0.3)
82
+ rake (13.4.2)
83
+ rdoc (7.2.0)
82
84
  erb
83
85
  psych (>= 4.0.0)
84
86
  tsort
85
- regexp_parser (2.11.3)
87
+ regexp_parser (2.12.0)
86
88
  reline (0.6.3)
87
89
  io-console (~> 0.5)
88
90
  rspec (3.13.2)
@@ -94,33 +96,34 @@ GEM
94
96
  rspec-expectations (3.13.5)
95
97
  diff-lcs (>= 1.2.0, < 2.0)
96
98
  rspec-support (~> 3.13.0)
97
- rspec-mocks (3.13.7)
99
+ rspec-mocks (3.13.8)
98
100
  diff-lcs (>= 1.2.0, < 2.0)
99
101
  rspec-support (~> 3.13.0)
100
- rspec-support (3.13.6)
101
- rubocop (1.82.1)
102
+ rspec-support (3.13.7)
103
+ rubocop (1.88.0)
102
104
  json (~> 2.3)
103
105
  language_server-protocol (~> 3.17.0.2)
104
106
  lint_roller (~> 1.1.0)
105
- parallel (~> 1.10)
107
+ parallel (>= 1.10)
106
108
  parser (>= 3.3.0.2)
107
109
  rainbow (>= 2.2.2, < 4.0)
108
110
  regexp_parser (>= 2.9.3, < 3.0)
109
- rubocop-ast (>= 1.48.0, < 2.0)
111
+ rubocop-ast (>= 1.49.0, < 2.0)
110
112
  ruby-progressbar (~> 1.7)
111
113
  unicode-display_width (>= 2.4.0, < 4.0)
112
- rubocop-ast (1.49.0)
114
+ rubocop-ast (1.49.1)
113
115
  parser (>= 3.3.7.2)
114
116
  prism (~> 1.7)
115
- rubocop-capybara (2.22.1)
117
+ rubocop-capybara (2.23.0)
116
118
  lint_roller (~> 1.1)
117
- rubocop (~> 1.72, >= 1.72.1)
119
+ rubocop (~> 1.81)
118
120
  rubocop-factory_bot (2.28.0)
119
121
  lint_roller (~> 1.1)
120
122
  rubocop (~> 1.72, >= 1.72.1)
121
- rubocop-rspec (3.9.0)
123
+ rubocop-rspec (3.10.2)
122
124
  lint_roller (~> 1.1)
123
- rubocop (~> 1.81)
125
+ regexp_parser (>= 2.0)
126
+ rubocop (~> 1.86, >= 1.86.2)
124
127
  rubocop-rspec_rails (2.32.0)
125
128
  lint_roller (~> 1.1)
126
129
  rubocop (~> 1.72, >= 1.72.1)
@@ -166,4 +169,4 @@ DEPENDENCIES
166
169
  simplecov
167
170
 
168
171
  BUNDLED WITH
169
- 4.0.3
172
+ 4.0.14
@@ -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 = '2.1.4'
7
+ s.version = '2.3.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 = ''
data/bin/console CHANGED
@@ -2,8 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'rubygems'
5
- require 'irb'
5
+ require 'pry'
6
6
 
7
7
  require File.expand_path("#{File.dirname(__FILE__)}/../config/boot")
8
8
 
9
- IRB.start
9
+ Pry.start
@@ -50,6 +50,7 @@ module Assembly
50
50
  # KDUcompress doesn’t support arbitrary image types, so we make a temporary tiff
51
51
  make_tmp_tiff(tmp_tiff_path)
52
52
  make_jp2(tmp_tiff_path)
53
+ clean_jp2_metadata
53
54
  end
54
55
 
55
56
  # create output response object, which is an Assembly::Image type object
@@ -129,6 +130,18 @@ module Assembly
129
130
  FileUtils.rm_rf(output_path)
130
131
  raise "JP2 creation command failed: #{jp2_command} with result #{result}"
131
132
  end
133
+
134
+ # Rebuild all EXIF meta information from scratch
135
+ # See https://exiftool.sourceforge.net/exiftool_pod.html#exiftool--exif:all--tagsfromfile--all:all--unsafe-bad.jpg
136
+ def clean_jp2_metadata
137
+ exif_command = "exiftool -all= -tagsfromfile @ -all:all -unsafe #{Shellwords.escape(output_path)}"
138
+ result = `#{exif_command}`
139
+ return if $CHILD_STATUS.success?
140
+
141
+ # Clean up any partial result
142
+ FileUtils.rm_rf(output_path)
143
+ raise "JP2 clean metadata command failed: #{exif_command} with result #{result}"
144
+ end
132
145
  end
133
146
  end
134
147
  end
@@ -17,6 +17,26 @@ module Assembly
17
17
  vips_image.width
18
18
  end
19
19
 
20
+ # @return [boolean] true if this image is a multi-page (e.g. a TIFF with multiple pages)
21
+ def multi_page?
22
+ return false unless mimetype == 'image/tiff'
23
+
24
+ vips_image.get('n-pages').to_i > 1
25
+ rescue Vips::Error
26
+ false
27
+ end
28
+
29
+ # Extract and save only the first page from a multi-image TIFF
30
+ # @param [String] output_path path to save the extracted first page
31
+ def extract_first_page(output_path)
32
+ raise "Cannot extract first page from mimetype #{mimetype}" unless mimetype == 'image/tiff'
33
+
34
+ first_page = Vips::Image.new_from_file(path, page: 0).autorot
35
+ first_page.write_to_file(output_path)
36
+
37
+ true
38
+ end
39
+
20
40
  # @return [string] full default jp2 path and filename that will be created from the given image
21
41
  # Example: given original file of '/dir/path_to_file.tif', gives '/dir/path_to_file.jp2'
22
42
  def jp2_filename
@@ -401,6 +401,50 @@ RSpec.describe Assembly::Image::Jp2Creator do
401
401
  end
402
402
  end
403
403
 
404
+ describe '#clean_jp2_metadata' do
405
+ let(:input_path) { TEST_TIF_INPUT_FILE }
406
+
407
+ before do
408
+ generate_test_image(input_path)
409
+ FileUtils.touch(jp2_output_file)
410
+ end
411
+
412
+ after { cleanup }
413
+
414
+ context 'when the output jp2 has a Coded Character Set IPTC tag' do
415
+ # Fixture is a real JP2 (created with opj_compress) so exiftool accepts IPTC writes to it.
416
+ # It lives outside spec/test_data/input/ so cleanup doesn't delete it.
417
+ let(:coded_charset_jp2) { 'spec/test_data/coded_charset_source.jp2' }
418
+
419
+ before do
420
+ FileUtils.cp(coded_charset_jp2, jp2_output_file)
421
+ system("exiftool -IPTC:CodedCharacterSet=UTF8 -overwrite_original '#{jp2_output_file}'")
422
+ end
423
+
424
+ it 'removes the Coded Character Set tag from the output file' do
425
+ expect(`exiftool '#{jp2_output_file}'`).to include('Coded Character Set')
426
+ jp2creator.send(:clean_jp2_metadata)
427
+ expect(`exiftool '#{jp2_output_file}'`).not_to include('Coded Character Set')
428
+ end
429
+ end
430
+
431
+ context 'when the exiftool command fails' do
432
+ before do
433
+ allow(jp2creator).to receive(:`).and_invoke(lambda { |_cmd|
434
+ system('false')
435
+ 'error output'
436
+ })
437
+ end
438
+
439
+ it 'raises an error and removes the output file' do
440
+ expect(File).to exist(jp2_output_file)
441
+ expect { jp2creator.send(:clean_jp2_metadata) }
442
+ .to raise_error(RuntimeError, /JP2 clean metadata command failed/)
443
+ expect(File).not_to exist(jp2_output_file)
444
+ end
445
+ end
446
+ end
447
+
404
448
  describe '#make_tmp_tiff' do
405
449
  let(:input_path) { 'spec/test_data/color_rgb_srgb_rot90cw.tif' }
406
450
  let(:plum) { [94.0, 58.0, 101.0] }
@@ -22,4 +22,91 @@ RSpec.describe Assembly::Image do
22
22
  end
23
23
  end
24
24
  end
25
+
26
+ describe '#multi_page?' do
27
+ context 'with a single page TIFF' do
28
+ before { generate_test_image(input_path) }
29
+
30
+ it 'returns false' do
31
+ expect(assembly_image.multi_page?).to be false
32
+ end
33
+ end
34
+
35
+ context 'with a multi-page TIFF' do
36
+ let(:input_path) { TEST_MULTIPAGE_TIF_FILE } # this image exists and is in our checked in codebase
37
+
38
+ it 'returns true' do
39
+ expect(assembly_image.multi_page?).to be true
40
+ end
41
+ end
42
+
43
+ context 'with a non-TIFF image' do
44
+ before { generate_test_image(input_path) }
45
+
46
+ let(:input_path) { TEST_JPEG_INPUT_FILE }
47
+
48
+ it 'returns false (JPEG cannot have multiple pages)' do
49
+ expect(assembly_image.multi_page?).to be false
50
+ end
51
+ end
52
+
53
+ context 'when Vips raises an error' do
54
+ before { generate_test_image(input_path) }
55
+
56
+ let(:input_path) { TEST_TIF_INPUT_FILE }
57
+
58
+ it 'rescues the error and returns false' do
59
+ allow(assembly_image.vips_image).to receive(:get).and_raise(Vips::Error, 'Test error')
60
+ expect(assembly_image.multi_page?).to be false
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#extract_first_page' do
66
+ let(:output_path) { File.join(TEST_OUTPUT_DIR, 'extracted.tif') }
67
+
68
+ after { FileUtils.rm_f(output_path) }
69
+
70
+ context 'with a single page TIFF' do
71
+ let(:input_path) { TEST_TIF_INPUT_FILE }
72
+
73
+ before { generate_test_image(input_path) }
74
+
75
+ it 'extracts and saves the first page' do
76
+ expect(assembly_image.multi_page?).to be false
77
+ result = assembly_image.extract_first_page(output_path)
78
+ expect(result).to be true
79
+ expect(File.exist?(output_path)).to be true
80
+ extracted = described_class.new(output_path)
81
+ expect(extracted.width).to eq assembly_image.width
82
+ expect(extracted.height).to eq assembly_image.height
83
+ expect(extracted.multi_page?).to be false
84
+ end
85
+ end
86
+
87
+ context 'with a multi-page TIFF' do
88
+ let(:input_path) { TEST_MULTIPAGE_TIF_FILE } # this is a test multi-page image that exists and is in our checked in codebase
89
+
90
+ it 'extracts only the first page' do
91
+ expect(assembly_image.multi_page?).to be true
92
+ result = assembly_image.extract_first_page(output_path)
93
+ expect(result).to be true
94
+ expect(File.exist?(output_path)).to be true
95
+ extracted = described_class.new(output_path)
96
+ expect(extracted.width).to eq assembly_image.width
97
+ expect(extracted.height).to eq assembly_image.height
98
+ expect(extracted.multi_page?).to be false
99
+ end
100
+ end
101
+
102
+ context 'with different image formats' do
103
+ before { generate_test_image(input_path) }
104
+
105
+ let(:input_path) { TEST_JPEG_INPUT_FILE }
106
+
107
+ it 'raises an error' do
108
+ expect { assembly_image.extract_first_page(output_path) }.to raise_error(RuntimeError, 'Cannot extract first page from mimetype image/jpeg')
109
+ end
110
+ end
111
+ end
25
112
  end
data/spec/spec_helper.rb CHANGED
@@ -19,6 +19,7 @@ TEST_INPUT_DIR = File.join(Assembly::PATH_TO_IMAGE_GEM, 'spec', 'test_data
19
19
  TEST_OUTPUT_DIR = File.join(Assembly::PATH_TO_IMAGE_GEM, 'spec', 'test_data', 'output')
20
20
  TEST_TIF_INPUT_FILE = File.join(TEST_INPUT_DIR, 'test.tif')
21
21
  TEST_JPEG_INPUT_FILE = File.join(TEST_INPUT_DIR, 'test.jpg')
22
+ TEST_MULTIPAGE_TIF_FILE = File.join(Assembly::PATH_TO_IMAGE_GEM, 'spec', 'test_data', 'shapes_multi_size.tif')
22
23
 
23
24
  RSpec.configure do |config|
24
25
  # rspec-expectations config goes here. You can use an alternate
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: 2.1.4
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Mangiafico
@@ -10,7 +10,7 @@ authors:
10
10
  - Tony Calavano
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2026-01-09 00:00:00.000000000 Z
13
+ date: 1980-01-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -213,9 +213,11 @@ files:
213
213
  - spec/assembly/image/jp2_creator_spec.rb
214
214
  - spec/assembly/image_spec.rb
215
215
  - spec/spec_helper.rb
216
+ - spec/test_data/coded_charset_source.jp2
216
217
  - spec/test_data/color_rgb_srgb_rot90cw.tif
217
218
  - spec/test_data/input/.empty
218
219
  - spec/test_data/output/.empty
220
+ - spec/test_data/shapes_multi_size.tif
219
221
  homepage: ''
220
222
  licenses: []
221
223
  metadata:
@@ -234,7 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
234
236
  - !ruby/object:Gem::Version
235
237
  version: '0'
236
238
  requirements: []
237
- rubygems_version: 3.6.2
239
+ rubygems_version: 4.0.6
238
240
  specification_version: 4
239
241
  summary: Ruby implementation of image services needed to prepare objects to be accessioned
240
242
  in SULAIR digital library