image_processing 2.0.3 → 2.2.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: 073bdcd7bc5e6fbb52b92f70de43d3b700316620dd23ec75b9aeac18bd5375b3
4
- data.tar.gz: 1678df6fd73afc2230fafd9c3f84b2d5174351819ef0f3961ed5206f2c6ae53b
3
+ metadata.gz: 5b790f3dd08c310a2413cb8cb6b33a9c602edb3509cdd1de2c8f94ebea702f17
4
+ data.tar.gz: 99d87927f2a6603939332f61292c2b2b582fecc5832b4562a92e8a5170e862d9
5
5
  SHA512:
6
- metadata.gz: e41739ac97beb0cb48cd97be766578cac1c14b4fd86fea0dd7401b835ecfbd2c018485d12ea0cb6d3c6cfcf0d4e0355d5c011770dd7f0c74aec26e7499cb306f
7
- data.tar.gz: 7618aa5091de6ddc2a01a189e548e508d862535ad8a7f4e400845c8696fcace78081f33efc343fa220ed04d13a0a71106a7e7c10141c107764270640ad30a0a6
6
+ metadata.gz: 00b2e0b0b250473b55e4ca415fd00f1c9fa50458e2c280a4110949838c344cb49c52b5afe023e0c75b8e435460f55517a1aeb8497ae1adf72fdca1de3fab59e2
7
+ data.tar.gz: a87c6791a8dbc06d5e40a6a64aaefd7803a382aac5071e238f7f7bface47d026405603bb6da9cc4534f9472f65d2e70bf4614cd75b9962506781a2818ad81b73
data/CHANGELOG.md CHANGED
@@ -1,4 +1,12 @@
1
- ## 2.0.3 (2026-06-08)
1
+ ## 2.2.0 (2026-09-29)
2
+
3
+ * Save in the `#convert` format when the destination path has no extension, such as a `/dev/fd/N` path (thanks to @flavorjones)
4
+
5
+ ## 2.1.0 (2026-09-01)
6
+
7
+ * [minimagick] Add `inherit_fds:` so a loader can name an already-open input (thanks to @flavorjones)
8
+
9
+ ## 2.0.3 (2026-08-06)
2
10
 
3
11
  * Prevent remote code execution when operation names come from user input, closing bypasses through the `#operation` meta-builder, `#method_missing`, and nested `#send` calls (reported by @szymonsec)
4
12
 
@@ -9,11 +9,20 @@ module ImageProcessing
9
9
  module MiniMagick
10
10
  extend Chainable
11
11
 
12
- def self.convert_shim(&block)
12
+ # mini_magick gained `inherit_fds:` on MiniMagick::Shell#execute in 5.4.0.
13
+ INHERIT_FDS_MINIMUM_VERSION = Gem::Version.new("5.4.0")
14
+
15
+ def self.convert_shim(inherit_fds: nil, &block)
16
+ if inherit_fds && ::MiniMagick.version < INHERIT_FDS_MINIMUM_VERSION
17
+ raise LoadError, "The `inherit_fds` loader option requires mini_magick #{INHERIT_FDS_MINIMUM_VERSION} or newer, but mini_magick #{::MiniMagick.version} is loaded. Please upgrade the gem."
18
+ end
19
+
20
+ options = inherit_fds ? { inherit_fds: inherit_fds } : {}
21
+
13
22
  if ::MiniMagick.respond_to?(:convert)
14
- ::MiniMagick.convert(&block)
23
+ ::MiniMagick.convert(**options, &block)
15
24
  else
16
- ::MiniMagick::Tool::Convert.new(&block)
25
+ ::MiniMagick::Tool::Convert.new(**options, &block)
17
26
  end
18
27
  end
19
28
 
@@ -37,12 +46,16 @@ module ImageProcessing
37
46
  # Initializes the image on disk into a MiniMagick::Tool object. Accepts
38
47
  # additional options related to loading the image (e.g. geometry).
39
48
  # Additionally auto-orients the image to be upright.
40
- def self.load_image(path_or_magick, loader: nil, page: nil, geometry: nil, auto_orient: true, **options)
49
+ # `inherit_fds` names IO objects the tool inherits, so the source may be a
50
+ # `/dev/fd/N` path. The source stays a path, so `loader`, `page` and
51
+ # `geometry` still apply to it, which they would not if the caller passed
52
+ # a pre-built MiniMagick::Tool carrying the descriptor.
53
+ def self.load_image(path_or_magick, loader: nil, page: nil, geometry: nil, auto_orient: true, inherit_fds: nil, **options)
41
54
  if path_or_magick.is_a?(::MiniMagick::Tool)
42
55
  magick = path_or_magick
43
56
  else
44
57
  source_path = path_or_magick
45
- magick = ::ImageProcessing::MiniMagick.convert_shim
58
+ magick = ::ImageProcessing::MiniMagick.convert_shim(inherit_fds: inherit_fds)
46
59
 
47
60
  Utils.apply_options(magick, **options)
48
61
 
@@ -60,11 +73,21 @@ module ImageProcessing
60
73
 
61
74
  # Calls the built ImageMagick command to perform processing and save
62
75
  # the result to disk. Accepts additional options related to saving the
63
- # image (e.g. quality).
64
- def self.save_image(magick, destination_path, allow_splitting: false, **options)
76
+ # image (e.g. quality). `format` names the output format when given,
77
+ # otherwise ImageMagick infers it from the path's extension.
78
+ def self.save_image(magick, destination_path, format = nil, allow_splitting: false, **options)
65
79
  Utils.apply_options(magick, **options)
66
80
 
67
- magick << destination_path
81
+ if format
82
+ # ImageMagick reads everything before the first colon as the
83
+ # format, so a format carrying a colon could redirect the output.
84
+ format = format.to_s
85
+ fail Error, "invalid format: #{format.inspect}" unless format.ascii_only? && format.match?(/\A\w+\z/)
86
+
87
+ magick << "#{format}:#{destination_path}"
88
+ else
89
+ magick << destination_path
90
+ end
68
91
  magick.call
69
92
 
70
93
  Utils.disallow_split_layers!(destination_path) unless allow_splitting
@@ -21,7 +21,11 @@ module ImageProcessing
21
21
  call_processor
22
22
  elsif destination
23
23
  handle_destination do
24
- call_processor(destination: destination)
24
+ if format && !determine_format(destination)
25
+ call_processor(destination: destination, format: format)
26
+ else
27
+ call_processor(destination: destination)
28
+ end
25
29
  end
26
30
  else
27
31
  create_tempfile do |tempfile|
@@ -1,7 +1,7 @@
1
1
  module ImageProcessing
2
2
  # Abstract class inherited by individual processors.
3
3
  class Processor
4
- def self.call(source:, loader:, operations:, saver:, destination: nil)
4
+ def self.call(source:, loader:, operations:, saver:, destination: nil, format: nil)
5
5
  unless source.is_a?(String) || source.is_a?(self::ACCUMULATOR_CLASS)
6
6
  fail Error, "invalid source: #{source.inspect}"
7
7
  end
@@ -19,7 +19,9 @@ module ImageProcessing
19
19
  accumulator = apply_operation(accumulator, operation)
20
20
  end
21
21
 
22
- if destination
22
+ if destination && format
23
+ save_image(accumulator, destination, format, **saver)
24
+ elsif destination
23
25
  save_image(accumulator, destination, **saver)
24
26
  else
25
27
  accumulator
@@ -1,3 +1,3 @@
1
1
  module ImageProcessing
2
- VERSION = "2.0.3"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -56,12 +56,24 @@ module ImageProcessing
56
56
 
57
57
  # Writes the Vips::Image object to disk. This starts the processing
58
58
  # pipeline defined in the Vips::Image object. Accepts additional
59
- # saver-specific options (e.g. quality).
60
- def self.save_image(image, path, saver: nil, quality: nil, **options)
59
+ # saver-specific options (e.g. quality). The saver is chosen by `format`
60
+ # when given, otherwise by the path's extension.
61
+ def self.save_image(image, path, format = nil, saver: nil, quality: nil, **options)
61
62
  options[:Q] = quality if quality
62
63
 
63
64
  if saver
64
65
  image.public_send(:"#{saver}save", path, **options)
66
+ elsif format
67
+ # Savers such as heifsave pick their encoding from the suffix (AV1
68
+ # for ".avif"), so save through a target, which takes the suffix
69
+ # separately from the path.
70
+ saver = ::Vips.vips_foreign_find_save_target(".#{format}")
71
+ fail ::Vips::Error, "No known saver for '#{format}'." unless saver
72
+
73
+ filename = ::Vips.p2str(::Vips.vips_filename_get_filename(path))
74
+ option_string = ::Vips.p2str(::Vips.vips_filename_get_options(path))
75
+ options = Utils.select_valid_options(saver, options)
76
+ image.write_to_target(::Vips::Target.new_to_file(filename), ".#{format}#{option_string}", **options)
65
77
  else
66
78
  options = Utils.select_valid_saver_options(path, options)
67
79
  image.write_to_file(path, **options)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_processing
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić