mini_magick 4.12.0 → 5.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 +4 -4
- data/README.md +558 -0
- data/lib/mini_magick/configuration.rb +35 -132
- data/lib/mini_magick/image/info.rb +7 -58
- data/lib/mini_magick/image.rb +34 -70
- data/lib/mini_magick/shell.rb +19 -45
- data/lib/mini_magick/tool.rb +41 -74
- data/lib/mini_magick/utilities.rb +4 -6
- data/lib/mini_magick/version.rb +2 -2
- data/lib/mini_magick.rb +18 -42
- metadata +20 -63
- data/lib/mini_gmagick.rb +0 -3
- data/lib/mini_magick/tool/animate.rb +0 -14
- data/lib/mini_magick/tool/compare.rb +0 -14
- data/lib/mini_magick/tool/composite.rb +0 -14
- data/lib/mini_magick/tool/conjure.rb +0 -14
- data/lib/mini_magick/tool/convert.rb +0 -14
- data/lib/mini_magick/tool/display.rb +0 -14
- data/lib/mini_magick/tool/identify.rb +0 -14
- data/lib/mini_magick/tool/import.rb +0 -14
- data/lib/mini_magick/tool/magick.rb +0 -14
- data/lib/mini_magick/tool/mogrify.rb +0 -14
- data/lib/mini_magick/tool/mogrify_restricted.rb +0 -15
- data/lib/mini_magick/tool/montage.rb +0 -14
- data/lib/mini_magick/tool/stream.rb +0 -14
@@ -5,12 +5,11 @@ module MiniMagick
|
|
5
5
|
module Configuration
|
6
6
|
|
7
7
|
##
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# Uses [GraphicsMagick](http://www.graphicsmagick.org/) instead of
|
9
|
+
# ImageMagick, by prefixing commands with `gm` instead of `magick`.
|
10
10
|
#
|
11
|
-
|
12
|
-
|
13
|
-
attr_accessor :processor_path
|
11
|
+
# @return [Boolean]
|
12
|
+
attr_accessor :graphicsmagick
|
14
13
|
|
15
14
|
##
|
16
15
|
# Adds a prefix to the CLI command.
|
@@ -24,23 +23,35 @@ module MiniMagick
|
|
24
23
|
attr_accessor :cli_prefix
|
25
24
|
|
26
25
|
##
|
27
|
-
#
|
28
|
-
#
|
26
|
+
# Adds environment variables to every CLI command call.
|
27
|
+
# For example, you could use it to set `LD_PRELOAD="/path/to/libsomething.so"`.
|
28
|
+
# Must be a hash of strings keyed to valid environment variable name strings.
|
29
|
+
# e.g. {'MY_ENV' => 'my value'}
|
29
30
|
#
|
30
|
-
# @return [
|
31
|
+
# @return [Hash]
|
31
32
|
#
|
32
|
-
attr_accessor :
|
33
|
+
attr_accessor :cli_env
|
34
|
+
|
33
35
|
##
|
34
|
-
#
|
35
|
-
#
|
36
|
+
# If set to true, Open3 will restrict system calls to access only
|
37
|
+
# environment variables defined in :cli_env, plus HOME, PATH, and LANG
|
38
|
+
# since those are required for such system calls. It will not pass on any
|
39
|
+
# other environment variables from the system.
|
36
40
|
#
|
37
41
|
# @return [Boolean]
|
38
42
|
#
|
39
|
-
|
43
|
+
attr_accessor :restricted_env
|
44
|
+
|
40
45
|
##
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
46
|
+
# If you don't want commands to take too long, you can set a timeout (in
|
47
|
+
# seconds).
|
48
|
+
#
|
49
|
+
# @return [Integer]
|
50
|
+
#
|
51
|
+
attr_accessor :timeout
|
52
|
+
##
|
53
|
+
# Logger for commands, default is `Logger.new($stdout)`, but you can
|
54
|
+
# override it, for example if you want the logs to be written to a file.
|
44
55
|
#
|
45
56
|
# @return [Logger]
|
46
57
|
#
|
@@ -53,146 +64,38 @@ module MiniMagick
|
|
53
64
|
#
|
54
65
|
attr_accessor :tmpdir
|
55
66
|
|
56
|
-
##
|
57
|
-
# If set to `true`, it will `identify` every newly created image, and raise
|
58
|
-
# `MiniMagick::Invalid` if the image is not valid. Useful for validating
|
59
|
-
# user input, although it adds a bit of overhead. Defaults to `true`.
|
60
|
-
#
|
61
|
-
# @return [Boolean]
|
62
|
-
#
|
63
|
-
attr_accessor :validate_on_create
|
64
|
-
##
|
65
|
-
# If set to `true`, it will `identify` every image that gets written (with
|
66
|
-
# {MiniMagick::Image#write}), and raise `MiniMagick::Invalid` if the image
|
67
|
-
# is not valid. Useful for validating that processing was sucessful,
|
68
|
-
# although it adds a bit of overhead. Defaults to `true`.
|
69
|
-
#
|
70
|
-
# @return [Boolean]
|
71
|
-
#
|
72
|
-
attr_accessor :validate_on_write
|
73
|
-
|
74
67
|
##
|
75
68
|
# If set to `false`, it will not raise errors when ImageMagick returns
|
76
69
|
# status code different than 0. Defaults to `true`.
|
77
70
|
#
|
78
71
|
# @return [Boolean]
|
79
72
|
#
|
80
|
-
attr_accessor :
|
73
|
+
attr_accessor :errors
|
81
74
|
|
82
75
|
##
|
83
|
-
#
|
84
|
-
#
|
85
|
-
|
86
|
-
#
|
87
|
-
# @return [String]
|
88
|
-
#
|
89
|
-
attr_accessor :shell_api
|
76
|
+
# If set to `false`, it will not forward warnings from ImageMagick to
|
77
|
+
# standard error.
|
78
|
+
attr_accessor :warnings
|
90
79
|
|
91
80
|
def self.extended(base)
|
92
81
|
base.tmpdir = Dir.tmpdir
|
93
|
-
base.
|
94
|
-
base.validate_on_write = true
|
95
|
-
base.whiny = true
|
96
|
-
base.shell_api = "open3"
|
82
|
+
base.errors = true
|
97
83
|
base.logger = Logger.new($stdout).tap { |l| l.level = Logger::INFO }
|
84
|
+
base.warnings = true
|
85
|
+
base.cli_env = {}.freeze
|
86
|
+
base.restricted_env = false
|
87
|
+
base.graphicsmagick = false
|
98
88
|
end
|
99
89
|
|
100
90
|
##
|
101
91
|
# @yield [self]
|
102
92
|
# @example
|
103
93
|
# MiniMagick.configure do |config|
|
104
|
-
# config.cli = :graphicsmagick
|
105
94
|
# config.timeout = 5
|
106
95
|
# end
|
107
96
|
#
|
108
97
|
def configure
|
109
98
|
yield self
|
110
99
|
end
|
111
|
-
|
112
|
-
CLI_DETECTION = {
|
113
|
-
imagemagick7: "magick",
|
114
|
-
imagemagick: "mogrify",
|
115
|
-
graphicsmagick: "gm",
|
116
|
-
}
|
117
|
-
|
118
|
-
# @private (for backwards compatibility)
|
119
|
-
def processor
|
120
|
-
@processor ||= CLI_DETECTION.values.detect do |processor|
|
121
|
-
MiniMagick::Utilities.which(processor)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
# @private (for backwards compatibility)
|
126
|
-
def processor=(processor)
|
127
|
-
@processor = processor.to_s
|
128
|
-
|
129
|
-
unless CLI_DETECTION.value?(@processor)
|
130
|
-
raise ArgumentError,
|
131
|
-
"processor has to be set to either \"magick\", \"mogrify\" or \"gm\"" \
|
132
|
-
", was set to #{@processor.inspect}"
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
##
|
137
|
-
# Get [ImageMagick](http://www.imagemagick.org) or
|
138
|
-
# [GraphicsMagick](http://www.graphicsmagick.org).
|
139
|
-
#
|
140
|
-
# @return [Symbol] `:imagemagick` or `:graphicsmagick`
|
141
|
-
#
|
142
|
-
def cli
|
143
|
-
if instance_variable_defined?("@cli")
|
144
|
-
instance_variable_get("@cli")
|
145
|
-
else
|
146
|
-
cli = CLI_DETECTION.key(processor) or
|
147
|
-
fail MiniMagick::Error, "You must have ImageMagick or GraphicsMagick installed"
|
148
|
-
|
149
|
-
instance_variable_set("@cli", cli)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
##
|
154
|
-
# Set whether you want to use [ImageMagick](http://www.imagemagick.org) or
|
155
|
-
# [GraphicsMagick](http://www.graphicsmagick.org).
|
156
|
-
#
|
157
|
-
def cli=(value)
|
158
|
-
@cli = value
|
159
|
-
|
160
|
-
if not CLI_DETECTION.key?(@cli)
|
161
|
-
raise ArgumentError,
|
162
|
-
"CLI has to be set to either :imagemagick, :imagemagick7 or :graphicsmagick" \
|
163
|
-
", was set to #{@cli.inspect}"
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
##
|
168
|
-
# If you set the path of CLI tools, you can get the path of the
|
169
|
-
# executables.
|
170
|
-
#
|
171
|
-
# @return [String]
|
172
|
-
#
|
173
|
-
def cli_path
|
174
|
-
if instance_variable_defined?("@cli_path")
|
175
|
-
instance_variable_get("@cli_path")
|
176
|
-
else
|
177
|
-
processor_path = instance_variable_get("@processor_path") if instance_variable_defined?("@processor_path")
|
178
|
-
|
179
|
-
instance_variable_set("@cli_path", processor_path)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
##
|
184
|
-
# When set to `true`, it outputs each command to STDOUT in their shell
|
185
|
-
# version.
|
186
|
-
#
|
187
|
-
def debug=(value)
|
188
|
-
warn "MiniMagick.debug is deprecated and will be removed in MiniMagick 5. Use `MiniMagick.logger.level = Logger::DEBUG` instead."
|
189
|
-
logger.level = value ? Logger::DEBUG : Logger::INFO
|
190
|
-
end
|
191
|
-
|
192
|
-
# Backwards compatibility
|
193
|
-
def reload_tools
|
194
|
-
warn "MiniMagick.reload_tools is deprecated because it is no longer necessary"
|
195
|
-
end
|
196
|
-
|
197
100
|
end
|
198
101
|
end
|
@@ -17,8 +17,6 @@ module MiniMagick
|
|
17
17
|
cheap_info(value)
|
18
18
|
when "colorspace"
|
19
19
|
colorspace
|
20
|
-
when "mime_type"
|
21
|
-
mime_type
|
22
20
|
when "resolution"
|
23
21
|
resolution(*args)
|
24
22
|
when "signature"
|
@@ -27,8 +25,6 @@ module MiniMagick
|
|
27
25
|
raw_exif(value)
|
28
26
|
when "exif"
|
29
27
|
exif
|
30
|
-
when "details"
|
31
|
-
details
|
32
28
|
when "data"
|
33
29
|
data
|
34
30
|
else
|
@@ -76,10 +72,6 @@ module MiniMagick
|
|
76
72
|
@info["colorspace"] ||= self["%r"]
|
77
73
|
end
|
78
74
|
|
79
|
-
def mime_type
|
80
|
-
"image/#{self["format"].downcase}"
|
81
|
-
end
|
82
|
-
|
83
75
|
def resolution(unit = nil)
|
84
76
|
output = identify do |b|
|
85
77
|
b.units unit if unit
|
@@ -100,20 +92,12 @@ module MiniMagick
|
|
100
92
|
output.each_line do |line|
|
101
93
|
line = line.chomp("\n")
|
102
94
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
key, value = match.post_match.split("=", 2)
|
107
|
-
value = decode_comma_separated_ascii_characters(value) if ASCII_ENCODED_EXIF_KEYS.include?(key)
|
108
|
-
hash[key] = value
|
109
|
-
else
|
110
|
-
hash[hash.keys.last] << "\n#{line}"
|
111
|
-
end
|
112
|
-
when :graphicsmagick
|
113
|
-
next if line == "unknown"
|
114
|
-
key, value = line.split("=", 2)
|
115
|
-
value.gsub!("\\012", "\n") # convert "\012" characters to newlines
|
95
|
+
if match = line.match(/^exif:/)
|
96
|
+
key, value = match.post_match.split("=", 2)
|
97
|
+
value = decode_comma_separated_ascii_characters(value) if ASCII_ENCODED_EXIF_KEYS.include?(key)
|
116
98
|
hash[key] = value
|
99
|
+
else
|
100
|
+
hash[hash.keys.last] << "\n#{line}"
|
117
101
|
end
|
118
102
|
end
|
119
103
|
|
@@ -129,44 +113,9 @@ module MiniMagick
|
|
129
113
|
@info["signature"] ||= self["%#"]
|
130
114
|
end
|
131
115
|
|
132
|
-
def details
|
133
|
-
warn "[MiniMagick] MiniMagick::Image#details has been deprecated, as it was causing too many parsing errors. You should use MiniMagick::Image#data instead, which differs in a way that the keys are in camelcase." if MiniMagick.imagemagick? || MiniMagick.imagemagick7?
|
134
|
-
|
135
|
-
@info["details"] ||= (
|
136
|
-
details_string = identify(&:verbose)
|
137
|
-
key_stack = []
|
138
|
-
details_string.lines.to_a[1..-1].each_with_object({}) do |line, details_hash|
|
139
|
-
next if !line.valid_encoding? || line.strip.length.zero?
|
140
|
-
|
141
|
-
level = line[/^\s*/].length / 2 - 1
|
142
|
-
if level >= 0
|
143
|
-
key_stack.pop until key_stack.size <= level
|
144
|
-
else
|
145
|
-
# Some metadata, such as SVG clipping paths, will be saved without
|
146
|
-
# indentation, resulting in a level of -1
|
147
|
-
last_key = details_hash.keys.last
|
148
|
-
details_hash[last_key] = '' if details_hash[last_key].empty?
|
149
|
-
details_hash[last_key] << line
|
150
|
-
next
|
151
|
-
end
|
152
|
-
|
153
|
-
key, _, value = line.partition(/:[\s]/).map(&:strip)
|
154
|
-
hash = key_stack.inject(details_hash) { |_hash, _key| _hash.fetch(_key) }
|
155
|
-
if value.empty?
|
156
|
-
hash[key] = {}
|
157
|
-
key_stack.push key
|
158
|
-
else
|
159
|
-
hash[key] = value
|
160
|
-
end
|
161
|
-
end
|
162
|
-
)
|
163
|
-
end
|
164
|
-
|
165
116
|
def data
|
166
|
-
raise Error, "MiniMagick::Image#data isn't supported on GraphicsMagick. Use MiniMagick::Image#details instead." if MiniMagick.graphicsmagick?
|
167
|
-
|
168
117
|
@info["data"] ||= (
|
169
|
-
json = MiniMagick
|
118
|
+
json = MiniMagick.convert do |convert|
|
170
119
|
convert << path
|
171
120
|
convert << "json:"
|
172
121
|
end
|
@@ -178,7 +127,7 @@ module MiniMagick
|
|
178
127
|
end
|
179
128
|
|
180
129
|
def identify
|
181
|
-
MiniMagick
|
130
|
+
MiniMagick.identify do |builder|
|
182
131
|
yield builder if block_given?
|
183
132
|
builder << path
|
184
133
|
end
|
data/lib/mini_magick/image.rb
CHANGED
@@ -51,11 +51,11 @@ module MiniMagick
|
|
51
51
|
#
|
52
52
|
def self.import_pixels(blob, columns, rows, depth, map, format = 'png')
|
53
53
|
# Create an image object with the raw pixel data string:
|
54
|
-
|
54
|
+
read(blob, ".dat").tap do |image|
|
55
55
|
output_path = image.path.sub(/\.\w+$/, ".#{format}")
|
56
56
|
# Use ImageMagick to convert the raw data file to an image file of the
|
57
57
|
# desired format:
|
58
|
-
MiniMagick
|
58
|
+
MiniMagick.convert do |convert|
|
59
59
|
convert.size "#{columns}x#{rows}"
|
60
60
|
convert.depth depth
|
61
61
|
convert << "#{map}:#{image.path}"
|
@@ -79,33 +79,15 @@ module MiniMagick
|
|
79
79
|
# @param options [Hash] Specify options for the open method
|
80
80
|
# @return [MiniMagick::Image] The loaded image
|
81
81
|
#
|
82
|
-
def self.open(path_or_url, ext = nil, options
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
if path_or_url.respond_to?(:open)
|
88
|
-
path_or_url
|
89
|
-
elsif path_or_url.respond_to?(:to_str) &&
|
90
|
-
%r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ path_or_url &&
|
91
|
-
(uri = URI.parse(path_or_url)).respond_to?(:open)
|
92
|
-
uri
|
93
|
-
else
|
94
|
-
options = { binmode: true }.merge(options)
|
95
|
-
Pathname(path_or_url)
|
96
|
-
end
|
97
|
-
|
98
|
-
if openable.is_a?(URI::Generic)
|
99
|
-
ext ||= File.extname(openable.path)
|
100
|
-
else
|
101
|
-
ext ||= File.extname(openable.to_s)
|
102
|
-
end
|
103
|
-
ext.sub!(/:.*/, '') # hack for filenames or URLs that include a colon
|
104
|
-
|
105
|
-
if openable.is_a?(URI::Generic)
|
106
|
-
openable.open(options) { |file| read(file, ext) }
|
82
|
+
def self.open(path_or_url, ext = nil, **options)
|
83
|
+
if path_or_url.to_s =~ %r{\A(https?|ftp)://}
|
84
|
+
uri = URI(path_or_url)
|
85
|
+
ext ||= File.extname(uri.path).sub(/:.*/, '') # handle URL including a colon
|
86
|
+
uri.open(options) { |file| read(file, ext) }
|
107
87
|
else
|
108
|
-
|
88
|
+
pathname = Pathname(path_or_url)
|
89
|
+
ext ||= File.extname(pathname.to_s)
|
90
|
+
pathname.open(binmode: true, **options) { |file| read(file, ext) }
|
109
91
|
end
|
110
92
|
end
|
111
93
|
|
@@ -118,18 +100,14 @@ module MiniMagick
|
|
118
100
|
# we have a good tempfile.
|
119
101
|
#
|
120
102
|
# @param ext [String] Specify the extension you want to read it as
|
121
|
-
# @param validate [Boolean] If false, skips validation of the created
|
122
|
-
# image. Defaults to true.
|
123
103
|
# @yield [Tempfile] You can #write bits to this object to create the new
|
124
104
|
# Image
|
125
105
|
# @return [MiniMagick::Image] The created image
|
126
106
|
#
|
127
|
-
def self.create(ext = nil,
|
107
|
+
def self.create(ext = nil, &block)
|
128
108
|
tempfile = MiniMagick::Utilities.tempfile(ext.to_s.downcase, &block)
|
129
109
|
|
130
|
-
new(tempfile.path, tempfile)
|
131
|
-
image.validate! if validate
|
132
|
-
end
|
110
|
+
new(tempfile.path, tempfile)
|
133
111
|
end
|
134
112
|
|
135
113
|
##
|
@@ -164,7 +142,7 @@ module MiniMagick
|
|
164
142
|
# which creates a temporary file for you and protects your original.
|
165
143
|
#
|
166
144
|
# @param input_path [String, Pathname] The location of an image file
|
167
|
-
# @yield [MiniMagick::Tool
|
145
|
+
# @yield [MiniMagick::Tool] If block is given, {#combine_options}
|
168
146
|
# is called.
|
169
147
|
#
|
170
148
|
def initialize(input_path, tempfile = nil, &block)
|
@@ -228,10 +206,6 @@ module MiniMagick
|
|
228
206
|
#
|
229
207
|
attribute :type, "format"
|
230
208
|
##
|
231
|
-
# @return [String]
|
232
|
-
#
|
233
|
-
attribute :mime_type
|
234
|
-
##
|
235
209
|
# @return [Integer]
|
236
210
|
#
|
237
211
|
attribute :width
|
@@ -274,7 +248,7 @@ module MiniMagick
|
|
274
248
|
#
|
275
249
|
attribute :resolution
|
276
250
|
##
|
277
|
-
# Returns the message digest of this image as a SHA-256,
|
251
|
+
# Returns the message digest of this image as a SHA-256, hexadecimal
|
278
252
|
# encoded string. This signature uniquely identifies the image and is
|
279
253
|
# convenient for determining if an image has been modified or whether two
|
280
254
|
# images are identical.
|
@@ -286,17 +260,10 @@ module MiniMagick
|
|
286
260
|
#
|
287
261
|
attribute :signature
|
288
262
|
##
|
289
|
-
# Returns the
|
290
|
-
# ImageMagick.
|
263
|
+
# Returns the result of converting the image to JSON format.
|
291
264
|
#
|
292
265
|
# @return [Hash]
|
293
266
|
attribute :data
|
294
|
-
##
|
295
|
-
# Returns the information from `identify -verbose` in a Hash format, for
|
296
|
-
# GraphicsMagick.
|
297
|
-
#
|
298
|
-
# @return [Hash]
|
299
|
-
attribute :details
|
300
267
|
|
301
268
|
##
|
302
269
|
# Use this method if you want to access raw Identify's format API.
|
@@ -367,7 +334,7 @@ module MiniMagick
|
|
367
334
|
# @return [Array] Matrix of each color of each pixel
|
368
335
|
def get_pixels(map="RGB")
|
369
336
|
raise ArgumentError, "Invalid map value" unless ["RGB", "RGBA"].include?(map)
|
370
|
-
convert = MiniMagick
|
337
|
+
convert = MiniMagick.convert
|
371
338
|
convert << path
|
372
339
|
convert.depth(8)
|
373
340
|
convert << "#{map}:-"
|
@@ -397,10 +364,10 @@ module MiniMagick
|
|
397
364
|
# @example
|
398
365
|
# # It is given in readme.md file
|
399
366
|
##
|
400
|
-
def self.get_image_from_pixels(pixels, dimension, map, depth,
|
367
|
+
def self.get_image_from_pixels(pixels, dimension, map, depth, format)
|
401
368
|
pixels = pixels.flatten
|
402
369
|
blob = pixels.pack('C*')
|
403
|
-
import_pixels(blob, *dimension, depth, map,
|
370
|
+
import_pixels(blob, *dimension, depth, map, format)
|
404
371
|
end
|
405
372
|
|
406
373
|
##
|
@@ -426,7 +393,7 @@ module MiniMagick
|
|
426
393
|
# will convert all pages.
|
427
394
|
# @param read_opts [Hash] Any read options to be passed to ImageMagick
|
428
395
|
# for example: image.format('jpg', page, {density: '300'})
|
429
|
-
# @yield [MiniMagick::Tool
|
396
|
+
# @yield [MiniMagick::Tool] It optionally yields the command,
|
430
397
|
# if you want to add something.
|
431
398
|
# @return [self]
|
432
399
|
#
|
@@ -441,7 +408,7 @@ module MiniMagick
|
|
441
408
|
input_path = path.dup
|
442
409
|
input_path << "[#{page}]" if page && !layer?
|
443
410
|
|
444
|
-
MiniMagick
|
411
|
+
MiniMagick.convert do |convert|
|
445
412
|
read_opts.each do |opt, val|
|
446
413
|
convert.send(opt.to_s, val)
|
447
414
|
end
|
@@ -477,7 +444,7 @@ module MiniMagick
|
|
477
444
|
# c.background "blue"
|
478
445
|
# end
|
479
446
|
#
|
480
|
-
# @yield [MiniMagick::
|
447
|
+
# @yield [MiniMagick::Command]
|
481
448
|
# @see http://www.imagemagick.org/script/mogrify.php
|
482
449
|
# @return [self]
|
483
450
|
#
|
@@ -498,8 +465,11 @@ module MiniMagick
|
|
498
465
|
end
|
499
466
|
end
|
500
467
|
|
501
|
-
|
502
|
-
|
468
|
+
##
|
469
|
+
# Prevents ruby from calling `#to_ary` on the image when checking if it's a
|
470
|
+
# splattable data structure in certain cases.
|
471
|
+
def respond_to_missing?(name, include_all)
|
472
|
+
false
|
503
473
|
end
|
504
474
|
|
505
475
|
##
|
@@ -514,7 +484,7 @@ module MiniMagick
|
|
514
484
|
case output_to
|
515
485
|
when String, Pathname
|
516
486
|
if layer?
|
517
|
-
MiniMagick
|
487
|
+
MiniMagick.convert do |builder|
|
518
488
|
builder << path
|
519
489
|
builder << output_to
|
520
490
|
end
|
@@ -541,7 +511,7 @@ module MiniMagick
|
|
541
511
|
def composite(other_image, output_extension = type.downcase, mask = nil)
|
542
512
|
output_tempfile = MiniMagick::Utilities.tempfile(".#{output_extension}")
|
543
513
|
|
544
|
-
MiniMagick
|
514
|
+
MiniMagick.composite do |composite|
|
545
515
|
yield composite if block_given?
|
546
516
|
composite << other_image.path
|
547
517
|
composite << path
|
@@ -583,27 +553,21 @@ module MiniMagick
|
|
583
553
|
# b.verbose
|
584
554
|
# end # runs `identify -verbose image.jpg`
|
585
555
|
# @return [String] Output from `identify`
|
586
|
-
# @yield [MiniMagick::Tool
|
556
|
+
# @yield [MiniMagick::Tool]
|
587
557
|
#
|
588
558
|
def identify
|
589
|
-
MiniMagick
|
559
|
+
MiniMagick.identify do |builder|
|
590
560
|
yield builder if block_given?
|
591
561
|
builder << path
|
592
562
|
end
|
593
563
|
end
|
594
564
|
|
595
|
-
# @private
|
596
|
-
def run_command(tool_name, *args)
|
597
|
-
MiniMagick::Tool.const_get(tool_name.capitalize).new do |builder|
|
598
|
-
args.each do |arg|
|
599
|
-
builder << arg
|
600
|
-
end
|
601
|
-
end
|
602
|
-
end
|
603
|
-
|
604
565
|
def mogrify(page = nil)
|
605
|
-
MiniMagick
|
566
|
+
MiniMagick.mogrify do |builder|
|
606
567
|
yield builder if block_given?
|
568
|
+
if builder.args.include?("-format")
|
569
|
+
fail MiniMagick::Error, "you must call #format on a MiniMagick::Image directly"
|
570
|
+
end
|
607
571
|
builder << (page ? "#{path}[#{page}]" : path)
|
608
572
|
end
|
609
573
|
|
data/lib/mini_magick/shell.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "
|
1
|
+
require "open3"
|
2
2
|
require "benchmark"
|
3
3
|
|
4
4
|
module MiniMagick
|
@@ -10,64 +10,38 @@ module MiniMagick
|
|
10
10
|
#
|
11
11
|
class Shell
|
12
12
|
|
13
|
-
def run(command,
|
14
|
-
stdout, stderr, status = execute(command,
|
13
|
+
def run(command, errors: MiniMagick.errors, warnings: MiniMagick.warnings, **options)
|
14
|
+
stdout, stderr, status = execute(command, **options)
|
15
15
|
|
16
|
-
if status != 0
|
17
|
-
|
16
|
+
if status != 0
|
17
|
+
if stderr.include?("time limit exceeded")
|
18
|
+
fail MiniMagick::TimeoutError, "`#{command.join(" ")}` has timed out"
|
19
|
+
elsif errors
|
20
|
+
fail MiniMagick::Error, "`#{command.join(" ")}` failed with status: #{status.inspect} and error:\n#{stderr}"
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
|
-
$stderr.print(stderr)
|
24
|
+
$stderr.print(stderr) if warnings
|
21
25
|
|
22
26
|
[stdout, stderr, status]
|
23
27
|
end
|
24
28
|
|
25
|
-
def execute(command,
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
def execute(command, stdin: "", timeout: MiniMagick.timeout)
|
30
|
+
env = MiniMagick.restricted_env ? ENV.slice("HOME", "PATH", "LANG") : {}
|
31
|
+
env.merge!(MiniMagick.cli_env)
|
32
|
+
env["MAGICK_TIME_LIMIT"] = timeout.to_s if timeout
|
33
|
+
|
34
|
+
stdout, stderr, status = log(command.join(" ")) do
|
35
|
+
Open3.capture3(env, *command, stdin_data: stdin, unsetenv_others: MiniMagick.restricted_env)
|
36
|
+
end
|
30
37
|
|
31
|
-
[stdout, stderr, status
|
38
|
+
[stdout, stderr, status&.exitstatus]
|
32
39
|
rescue Errno::ENOENT, IOError
|
33
40
|
["", "executable not found: \"#{command.first}\"", 127]
|
34
41
|
end
|
35
42
|
|
36
43
|
private
|
37
44
|
|
38
|
-
def execute_open3(command, options = {})
|
39
|
-
require "open3"
|
40
|
-
|
41
|
-
# We would ideally use Open3.capture3, but it wouldn't allow us to
|
42
|
-
# terminate the command after timing out.
|
43
|
-
Open3.popen3(*command) do |in_w, out_r, err_r, thread|
|
44
|
-
[in_w, out_r, err_r].each(&:binmode)
|
45
|
-
stdout_reader = Thread.new { out_r.read }
|
46
|
-
stderr_reader = Thread.new { err_r.read }
|
47
|
-
begin
|
48
|
-
in_w.write options[:stdin].to_s
|
49
|
-
rescue Errno::EPIPE
|
50
|
-
end
|
51
|
-
in_w.close
|
52
|
-
|
53
|
-
unless thread.join(MiniMagick.timeout)
|
54
|
-
Process.kill("TERM", thread.pid) rescue nil
|
55
|
-
Process.waitpid(thread.pid) rescue nil
|
56
|
-
raise Timeout::Error, "MiniMagick command timed out: #{command}"
|
57
|
-
end
|
58
|
-
|
59
|
-
[stdout_reader.value, stderr_reader.value, thread.value]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def execute_posix_spawn(command, options = {})
|
64
|
-
require "posix-spawn"
|
65
|
-
child = POSIX::Spawn::Child.new(*command, input: options[:stdin].to_s, timeout: MiniMagick.timeout)
|
66
|
-
[child.out, child.err, child.status]
|
67
|
-
rescue POSIX::Spawn::TimeoutExceeded
|
68
|
-
raise Timeout::Error, "MiniMagick command timed out: #{command}"
|
69
|
-
end
|
70
|
-
|
71
45
|
def log(command, &block)
|
72
46
|
value = nil
|
73
47
|
duration = Benchmark.realtime { value = block.call }
|