mini_magick 4.7.2 → 4.13.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +573 -0
- data/lib/mini_magick/configuration.rb +82 -35
- data/lib/mini_magick/image/info.rb +18 -5
- data/lib/mini_magick/image.rb +90 -17
- data/lib/mini_magick/shell.rb +27 -30
- data/lib/mini_magick/tool/magick.rb +14 -0
- data/lib/mini_magick/tool.rb +35 -19
- data/lib/mini_magick/utilities.rb +1 -1
- data/lib/mini_magick/version.rb +1 -1
- data/lib/mini_magick.rb +8 -0
- metadata +9 -50
@@ -5,24 +5,23 @@ module MiniMagick
|
|
5
5
|
module Configuration
|
6
6
|
|
7
7
|
##
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# @return [Symbol] `:imagemagick` or `:graphicsmagick`
|
8
|
+
# If you don't have the CLI tools in your PATH, you can set the path to the
|
9
|
+
# executables.
|
12
10
|
#
|
13
|
-
|
11
|
+
attr_writer :cli_path
|
14
12
|
# @private (for backwards compatibility)
|
15
|
-
attr_accessor :
|
13
|
+
attr_accessor :processor_path
|
16
14
|
|
17
15
|
##
|
18
|
-
#
|
19
|
-
#
|
16
|
+
# Adds a prefix to the CLI command.
|
17
|
+
# For example, you could use `firejail` to run all commands in a sandbox.
|
18
|
+
# Can be a string, or an array of strings.
|
19
|
+
# e.g. 'firejail', or ['firejail', '--force']
|
20
20
|
#
|
21
21
|
# @return [String]
|
22
|
+
# @return [Array<String>]
|
22
23
|
#
|
23
|
-
attr_accessor :
|
24
|
-
# @private (for backwards compatibility)
|
25
|
-
attr_accessor :processor_path
|
24
|
+
attr_accessor :cli_prefix
|
26
25
|
|
27
26
|
##
|
28
27
|
# If you don't want commands to take too long, you can set a timeout (in
|
@@ -32,12 +31,12 @@ module MiniMagick
|
|
32
31
|
#
|
33
32
|
attr_accessor :timeout
|
34
33
|
##
|
35
|
-
# When
|
34
|
+
# When get to `true`, it outputs each command to STDOUT in their shell
|
36
35
|
# version.
|
37
36
|
#
|
38
37
|
# @return [Boolean]
|
39
38
|
#
|
40
|
-
|
39
|
+
attr_reader :debug
|
41
40
|
##
|
42
41
|
# Logger for {#debug}, default is `MiniMagick::Logger.new(STDOUT)`, but
|
43
42
|
# you can override it, for example if you want the logs to be written to
|
@@ -46,6 +45,13 @@ module MiniMagick
|
|
46
45
|
# @return [Logger]
|
47
46
|
#
|
48
47
|
attr_accessor :logger
|
48
|
+
##
|
49
|
+
# Temporary directory used by MiniMagick, default is `Dir.tmpdir`, but
|
50
|
+
# you can override it.
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
#
|
54
|
+
attr_accessor :tmpdir
|
49
55
|
|
50
56
|
##
|
51
57
|
# If set to `true`, it will `identify` every newly created image, and raise
|
@@ -58,7 +64,7 @@ module MiniMagick
|
|
58
64
|
##
|
59
65
|
# If set to `true`, it will `identify` every image that gets written (with
|
60
66
|
# {MiniMagick::Image#write}), and raise `MiniMagick::Invalid` if the image
|
61
|
-
# is not valid. Useful for validating that processing was
|
67
|
+
# is not valid. Useful for validating that processing was successful,
|
62
68
|
# although it adds a bit of overhead. Defaults to `true`.
|
63
69
|
#
|
64
70
|
# @return [Boolean]
|
@@ -74,20 +80,17 @@ module MiniMagick
|
|
74
80
|
attr_accessor :whiny
|
75
81
|
|
76
82
|
##
|
77
|
-
#
|
78
|
-
#
|
79
|
-
|
80
|
-
#
|
81
|
-
# @return [String]
|
82
|
-
#
|
83
|
-
attr_accessor :shell_api
|
83
|
+
# If set to `false`, it will not forward warnings from ImageMagick to
|
84
|
+
# standard error.
|
85
|
+
attr_accessor :warnings
|
84
86
|
|
85
87
|
def self.extended(base)
|
88
|
+
base.tmpdir = Dir.tmpdir
|
86
89
|
base.validate_on_create = true
|
87
90
|
base.validate_on_write = true
|
88
91
|
base.whiny = true
|
89
|
-
base.shell_api = "open3"
|
90
92
|
base.logger = Logger.new($stdout).tap { |l| l.level = Logger::INFO }
|
93
|
+
base.warnings = true
|
91
94
|
end
|
92
95
|
|
93
96
|
##
|
@@ -102,51 +105,95 @@ module MiniMagick
|
|
102
105
|
yield self
|
103
106
|
end
|
104
107
|
|
108
|
+
CLI_DETECTION = {
|
109
|
+
imagemagick7: "magick",
|
110
|
+
imagemagick: "mogrify",
|
111
|
+
graphicsmagick: "gm",
|
112
|
+
}
|
113
|
+
|
114
|
+
# @private (for backwards compatibility)
|
105
115
|
def processor
|
106
|
-
@processor ||=
|
116
|
+
@processor ||= CLI_DETECTION.values.detect do |processor|
|
107
117
|
MiniMagick::Utilities.which(processor)
|
108
118
|
end
|
109
119
|
end
|
110
120
|
|
121
|
+
# @private (for backwards compatibility)
|
111
122
|
def processor=(processor)
|
112
123
|
@processor = processor.to_s
|
113
124
|
|
114
|
-
unless
|
125
|
+
unless CLI_DETECTION.value?(@processor)
|
115
126
|
raise ArgumentError,
|
116
|
-
"processor has to be set to either \"mogrify\" or \"gm\"" \
|
127
|
+
"processor has to be set to either \"magick\", \"mogrify\" or \"gm\"" \
|
117
128
|
", was set to #{@processor.inspect}"
|
118
129
|
end
|
119
130
|
end
|
120
131
|
|
132
|
+
##
|
133
|
+
# Get [ImageMagick](http://www.imagemagick.org) or
|
134
|
+
# [GraphicsMagick](http://www.graphicsmagick.org).
|
135
|
+
#
|
136
|
+
# @return [Symbol] `:imagemagick` or `:graphicsmagick`
|
137
|
+
#
|
121
138
|
def cli
|
122
|
-
@cli
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
139
|
+
if instance_variable_defined?("@cli")
|
140
|
+
instance_variable_get("@cli")
|
141
|
+
else
|
142
|
+
cli = CLI_DETECTION.key(processor) or
|
143
|
+
fail MiniMagick::Error, "You must have ImageMagick or GraphicsMagick installed"
|
144
|
+
|
145
|
+
instance_variable_set("@cli", cli)
|
146
|
+
end
|
129
147
|
end
|
130
148
|
|
149
|
+
##
|
150
|
+
# Set whether you want to use [ImageMagick](http://www.imagemagick.org) or
|
151
|
+
# [GraphicsMagick](http://www.graphicsmagick.org).
|
152
|
+
#
|
131
153
|
def cli=(value)
|
132
154
|
@cli = value
|
133
155
|
|
134
|
-
if not
|
156
|
+
if not CLI_DETECTION.key?(@cli)
|
135
157
|
raise ArgumentError,
|
136
|
-
"CLI has to be set to either :imagemagick or :graphicsmagick" \
|
158
|
+
"CLI has to be set to either :imagemagick, :imagemagick7 or :graphicsmagick" \
|
137
159
|
", was set to #{@cli.inspect}"
|
138
160
|
end
|
139
161
|
end
|
140
162
|
|
163
|
+
##
|
164
|
+
# If you set the path of CLI tools, you can get the path of the
|
165
|
+
# executables.
|
166
|
+
#
|
167
|
+
# @return [String]
|
168
|
+
#
|
141
169
|
def cli_path
|
142
|
-
@cli_path
|
170
|
+
if instance_variable_defined?("@cli_path")
|
171
|
+
instance_variable_get("@cli_path")
|
172
|
+
else
|
173
|
+
processor_path = instance_variable_get("@processor_path") if instance_variable_defined?("@processor_path")
|
174
|
+
|
175
|
+
instance_variable_set("@cli_path", processor_path)
|
176
|
+
end
|
143
177
|
end
|
144
178
|
|
179
|
+
##
|
180
|
+
# When set to `true`, it outputs each command to STDOUT in their shell
|
181
|
+
# version.
|
182
|
+
#
|
145
183
|
def debug=(value)
|
146
184
|
warn "MiniMagick.debug is deprecated and will be removed in MiniMagick 5. Use `MiniMagick.logger.level = Logger::DEBUG` instead."
|
147
185
|
logger.level = value ? Logger::DEBUG : Logger::INFO
|
148
186
|
end
|
149
187
|
|
188
|
+
def shell_api=(value)
|
189
|
+
warn "MiniMagick.shell_api is deprecated and will be removed in MiniMagick 5. The posix-spawn gem doesn't improve performance recent Ruby versions anymore, so support for it will be removed."
|
190
|
+
@shell_api = value
|
191
|
+
end
|
192
|
+
|
193
|
+
def shell_api
|
194
|
+
@shell_api || "open3"
|
195
|
+
end
|
196
|
+
|
150
197
|
# Backwards compatibility
|
151
198
|
def reload_tools
|
152
199
|
warn "MiniMagick.reload_tools is deprecated because it is no longer necessary"
|
@@ -42,7 +42,7 @@ module MiniMagick
|
|
42
42
|
|
43
43
|
def cheap_info(value)
|
44
44
|
@info.fetch(value) do
|
45
|
-
format, width, height, size = self["%m %w %h %b"].split(" ")
|
45
|
+
format, width, height, size = parse_warnings(self["%m %w %h %b"]).split(" ")
|
46
46
|
|
47
47
|
path = @path
|
48
48
|
path = path.match(/\[\d+\]$/).pre_match if path =~ /\[\d+\]$/
|
@@ -62,11 +62,23 @@ module MiniMagick
|
|
62
62
|
raise MiniMagick::Invalid, "image data can't be read"
|
63
63
|
end
|
64
64
|
|
65
|
+
def parse_warnings(raw_info)
|
66
|
+
return raw_info unless raw_info.split("\n").size > 1
|
67
|
+
|
68
|
+
raw_info.split("\n").each do |line|
|
69
|
+
# must match "%m %w %h %b"
|
70
|
+
return line if line.match?(/^[A-Z]+ \d+ \d+ \d+(|\.\d+)([KMGTPEZY]{0,1})B$/)
|
71
|
+
end
|
72
|
+
raise TypeError
|
73
|
+
end
|
74
|
+
|
65
75
|
def colorspace
|
66
76
|
@info["colorspace"] ||= self["%r"]
|
67
77
|
end
|
68
78
|
|
69
79
|
def mime_type
|
80
|
+
warn "[MiniMagick] MiniMagick::Image#mime_type has been deprecated, because it wasn't returning correct result for all formats ImageMagick supports. Unfortunately, returning the correct MIME type would be very slow, because it would require ImageMagick to read the whole file. It's better to use Marcel and MimeMagic gems, which are able to determine the MIME type just from the image header."
|
81
|
+
|
70
82
|
"image/#{self["format"].downcase}"
|
71
83
|
end
|
72
84
|
|
@@ -91,7 +103,7 @@ module MiniMagick
|
|
91
103
|
line = line.chomp("\n")
|
92
104
|
|
93
105
|
case MiniMagick.cli
|
94
|
-
when :imagemagick
|
106
|
+
when :imagemagick, :imagemagick7
|
95
107
|
if match = line.match(/^exif:/)
|
96
108
|
key, value = match.post_match.split("=", 2)
|
97
109
|
value = decode_comma_separated_ascii_characters(value) if ASCII_ENCODED_EXIF_KEYS.include?(key)
|
@@ -100,6 +112,7 @@ module MiniMagick
|
|
100
112
|
hash[hash.keys.last] << "\n#{line}"
|
101
113
|
end
|
102
114
|
when :graphicsmagick
|
115
|
+
next if line == "unknown"
|
103
116
|
key, value = line.split("=", 2)
|
104
117
|
value.gsub!("\\012", "\n") # convert "\012" characters to newlines
|
105
118
|
hash[key] = value
|
@@ -119,7 +132,7 @@ module MiniMagick
|
|
119
132
|
end
|
120
133
|
|
121
134
|
def details
|
122
|
-
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?
|
135
|
+
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?
|
123
136
|
|
124
137
|
@info["details"] ||= (
|
125
138
|
details_string = identify(&:verbose)
|
@@ -139,8 +152,8 @@ module MiniMagick
|
|
139
152
|
next
|
140
153
|
end
|
141
154
|
|
142
|
-
key, _, value = line.partition(/:[\s
|
143
|
-
hash = key_stack.inject(details_hash) { |
|
155
|
+
key, _, value = line.partition(/:[\s]/).map(&:strip)
|
156
|
+
hash = key_stack.inject(details_hash) { |_hash, _key| _hash.fetch(_key) }
|
144
157
|
if value.empty?
|
145
158
|
hash[key] = {}
|
146
159
|
key_stack.push key
|
data/lib/mini_magick/image.rb
CHANGED
@@ -15,7 +15,7 @@ module MiniMagick
|
|
15
15
|
# methods.
|
16
16
|
#
|
17
17
|
# Use this to pass in a stream object. Must respond to #read(size) or be a
|
18
|
-
# binary string object (
|
18
|
+
# binary string object (BLOB)
|
19
19
|
#
|
20
20
|
# Probably easier to use the {.open} method if you want to open a file or a
|
21
21
|
# URL.
|
@@ -76,20 +76,36 @@ module MiniMagick
|
|
76
76
|
# @param path_or_url [String] Either a local file path or a URL that
|
77
77
|
# open-uri can read
|
78
78
|
# @param ext [String] Specify the extension you want to read it as
|
79
|
+
# @param options [Hash] Specify options for the open method
|
79
80
|
# @return [MiniMagick::Image] The loaded image
|
80
81
|
#
|
81
|
-
def self.open(path_or_url, ext = nil)
|
82
|
-
ext
|
83
|
-
|
84
|
-
|
82
|
+
def self.open(path_or_url, ext = nil, options = {})
|
83
|
+
options, ext = ext, nil if ext.is_a?(Hash)
|
84
|
+
|
85
|
+
# Don't use Kernel#open, but reuse its logic
|
86
|
+
openable =
|
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
|
85
93
|
else
|
86
|
-
|
94
|
+
options = { binmode: true }.merge(options)
|
95
|
+
Pathname(path_or_url)
|
87
96
|
end
|
88
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
|
89
103
|
ext.sub!(/:.*/, '') # hack for filenames or URLs that include a colon
|
90
104
|
|
91
|
-
|
92
|
-
read(file, ext)
|
105
|
+
if openable.is_a?(URI::Generic)
|
106
|
+
openable.open(options) { |file| read(file, ext) }
|
107
|
+
else
|
108
|
+
openable.open(**options) { |file| read(file, ext) }
|
93
109
|
end
|
94
110
|
end
|
95
111
|
|
@@ -258,7 +274,7 @@ module MiniMagick
|
|
258
274
|
#
|
259
275
|
attribute :resolution
|
260
276
|
##
|
261
|
-
# Returns the message digest of this image as a SHA-256,
|
277
|
+
# Returns the message digest of this image as a SHA-256, hexadecimal
|
262
278
|
# encoded string. This signature uniquely identifies the image and is
|
263
279
|
# convenient for determining if an image has been modified or whether two
|
264
280
|
# images are identical.
|
@@ -324,13 +340,18 @@ module MiniMagick
|
|
324
340
|
#
|
325
341
|
# 1) one for each row of pixels
|
326
342
|
# 2) one for each column of pixels
|
327
|
-
# 3) three elements in the range 0-255, one for each of the RGB color channels
|
343
|
+
# 3) three or four elements in the range 0-255, one for each of the RGB(A) color channels
|
328
344
|
#
|
329
345
|
# @example
|
330
346
|
# img = MiniMagick::Image.open 'image.jpg'
|
331
347
|
# pixels = img.get_pixels
|
332
348
|
# pixels[3][2][1] # the green channel value from the 4th-row, 3rd-column pixel
|
333
349
|
#
|
350
|
+
# @example
|
351
|
+
# img = MiniMagick::Image.open 'image.jpg'
|
352
|
+
# pixels = img.get_pixels("RGBA")
|
353
|
+
# pixels[3][2][3] # the alpha channel value from the 4th-row, 3rd-column pixel
|
354
|
+
#
|
334
355
|
# It can also be called after applying transformations:
|
335
356
|
#
|
336
357
|
# @example
|
@@ -341,16 +362,22 @@ module MiniMagick
|
|
341
362
|
#
|
342
363
|
# In this example, all pixels in pix should now have equal R, G, and B values.
|
343
364
|
#
|
365
|
+
# @param map [String] A code for the mapping of the pixel data. Must be either
|
366
|
+
# 'RGB' or 'RGBA'. Default to 'RGB'
|
344
367
|
# @return [Array] Matrix of each color of each pixel
|
345
|
-
def get_pixels
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
368
|
+
def get_pixels(map="RGB")
|
369
|
+
raise ArgumentError, "Invalid map value" unless ["RGB", "RGBA"].include?(map)
|
370
|
+
convert = MiniMagick::Tool::Convert.new
|
371
|
+
convert << path
|
372
|
+
convert.depth(8)
|
373
|
+
convert << "#{map}:-"
|
374
|
+
|
375
|
+
# Do not use `convert.call` here. We need the whole binary (unstripped) output here.
|
376
|
+
shell = MiniMagick::Shell.new
|
377
|
+
output, * = shell.run(convert.command)
|
351
378
|
|
352
379
|
pixels_array = output.unpack("C*")
|
353
|
-
pixels = pixels_array.each_slice(
|
380
|
+
pixels = pixels_array.each_slice(map.length).each_slice(width).to_a
|
354
381
|
|
355
382
|
# deallocate large intermediary objects
|
356
383
|
output.clear
|
@@ -359,6 +386,23 @@ module MiniMagick
|
|
359
386
|
pixels
|
360
387
|
end
|
361
388
|
|
389
|
+
##
|
390
|
+
# This is used to create image from pixels. This might be required if you
|
391
|
+
# create pixels for some image processing reasons and you want to form
|
392
|
+
# image from those pixels.
|
393
|
+
#
|
394
|
+
# *DANGER*: This operation can be very expensive. Please try to use with
|
395
|
+
# caution.
|
396
|
+
#
|
397
|
+
# @example
|
398
|
+
# # It is given in readme.md file
|
399
|
+
##
|
400
|
+
def self.get_image_from_pixels(pixels, dimension, map, depth, mime_type)
|
401
|
+
pixels = pixels.flatten
|
402
|
+
blob = pixels.pack('C*')
|
403
|
+
import_pixels(blob, *dimension, depth, map, mime_type)
|
404
|
+
end
|
405
|
+
|
362
406
|
##
|
363
407
|
# This is used to change the format of the image. That is, from "tiff to
|
364
408
|
# jpg" or something like that. Once you run it, the instance is pointing to
|
@@ -417,6 +461,9 @@ module MiniMagick
|
|
417
461
|
@info.clear
|
418
462
|
|
419
463
|
self
|
464
|
+
rescue MiniMagick::Invalid, MiniMagick::Error => e
|
465
|
+
new_tempfile.unlink if new_tempfile && @tempfile != new_tempfile
|
466
|
+
raise e
|
420
467
|
end
|
421
468
|
|
422
469
|
##
|
@@ -568,5 +615,31 @@ module MiniMagick
|
|
568
615
|
def layer?
|
569
616
|
path =~ /\[\d+\]$/
|
570
617
|
end
|
618
|
+
|
619
|
+
##
|
620
|
+
# Compares if image width
|
621
|
+
# is greater than height
|
622
|
+
# ============
|
623
|
+
# | |
|
624
|
+
# | |
|
625
|
+
# ============
|
626
|
+
# @return [Boolean]
|
627
|
+
def landscape?
|
628
|
+
width > height
|
629
|
+
end
|
630
|
+
|
631
|
+
##
|
632
|
+
# Compares if image height
|
633
|
+
# is greater than width
|
634
|
+
# ======
|
635
|
+
# | |
|
636
|
+
# | |
|
637
|
+
# | |
|
638
|
+
# | |
|
639
|
+
# ======
|
640
|
+
# @return [Boolean]
|
641
|
+
def portrait?
|
642
|
+
height > width
|
643
|
+
end
|
571
644
|
end
|
572
645
|
end
|
data/lib/mini_magick/shell.rb
CHANGED
@@ -14,9 +14,10 @@ module MiniMagick
|
|
14
14
|
stdout, stderr, status = execute(command, stdin: options[:stdin])
|
15
15
|
|
16
16
|
if status != 0 && options.fetch(:whiny, MiniMagick.whiny)
|
17
|
-
fail MiniMagick::Error, "`#{command.join(" ")}` failed with error:\n#{stderr}"
|
17
|
+
fail MiniMagick::Error, "`#{command.join(" ")}` failed with status: #{status.inspect} and error:\n#{stderr}"
|
18
18
|
end
|
19
19
|
|
20
|
+
stderr = stderr.lines[2..-1].join if stderr.start_with? %(WARNING: The convert command is deprecated in IMv7)
|
20
21
|
$stderr.print(stderr) unless options[:stderr] == false
|
21
22
|
|
22
23
|
[stdout, stderr, status]
|
@@ -25,10 +26,10 @@ module MiniMagick
|
|
25
26
|
def execute(command, options = {})
|
26
27
|
stdout, stderr, status =
|
27
28
|
log(command.join(" ")) do
|
28
|
-
send("execute_#{MiniMagick.shell_api.
|
29
|
+
send("execute_#{MiniMagick.shell_api.tr("-", "_")}", command, options)
|
29
30
|
end
|
30
31
|
|
31
|
-
[stdout, stderr, status
|
32
|
+
[stdout, stderr, status&.exitstatus]
|
32
33
|
rescue Errno::ENOENT, IOError
|
33
34
|
["", "executable not found: \"#{command.first}\"", 127]
|
34
35
|
end
|
@@ -38,38 +39,34 @@ module MiniMagick
|
|
38
39
|
def execute_open3(command, options = {})
|
39
40
|
require "open3"
|
40
41
|
|
41
|
-
|
42
|
+
# We would ideally use Open3.capture3, but it wouldn't allow us to
|
43
|
+
# terminate the command after timing out.
|
44
|
+
Open3.popen3(*command) do |in_w, out_r, err_r, thread|
|
45
|
+
[in_w, out_r, err_r].each(&:binmode)
|
46
|
+
stdout_reader = Thread.new { out_r.read }
|
47
|
+
stderr_reader = Thread.new { err_r.read }
|
48
|
+
begin
|
49
|
+
in_w.write options[:stdin].to_s
|
50
|
+
rescue Errno::EPIPE
|
51
|
+
end
|
52
|
+
in_w.close
|
53
|
+
|
54
|
+
unless thread.join(MiniMagick.timeout)
|
55
|
+
Process.kill("TERM", thread.pid) rescue nil
|
56
|
+
Process.waitpid(thread.pid) rescue nil
|
57
|
+
raise Timeout::Error, "MiniMagick command timed out: #{command}"
|
58
|
+
end
|
42
59
|
|
43
|
-
|
60
|
+
[stdout_reader.value, stderr_reader.value, thread.value]
|
61
|
+
end
|
44
62
|
end
|
45
63
|
|
46
64
|
def execute_posix_spawn(command, options = {})
|
47
65
|
require "posix-spawn"
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
capture_command(in_w, out_r, err_r, subprocess_thread, options)
|
53
|
-
end
|
54
|
-
|
55
|
-
def capture_command(in_w, out_r, err_r, subprocess_thread, options)
|
56
|
-
[in_w, out_r, err_r].each(&:binmode)
|
57
|
-
stdout_reader = Thread.new { out_r.read }
|
58
|
-
stderr_reader = Thread.new { err_r.read }
|
59
|
-
begin
|
60
|
-
in_w.write options[:stdin].to_s
|
61
|
-
rescue Errno::EPIPE
|
62
|
-
end
|
63
|
-
in_w.close
|
64
|
-
|
65
|
-
Timeout.timeout(MiniMagick.timeout) { subprocess_thread.join }
|
66
|
-
|
67
|
-
[stdout_reader.value, stderr_reader.value, subprocess_thread.value]
|
68
|
-
rescue Timeout::Error => error
|
69
|
-
Process.kill("TERM", subprocess_thread.pid)
|
70
|
-
raise error
|
71
|
-
ensure
|
72
|
-
[out_r, err_r].each(&:close)
|
66
|
+
child = POSIX::Spawn::Child.new(*command, input: options[:stdin].to_s, timeout: MiniMagick.timeout)
|
67
|
+
[child.out, child.err, child.status]
|
68
|
+
rescue POSIX::Spawn::TimeoutExceeded
|
69
|
+
raise Timeout::Error, "MiniMagick command timed out: #{command}"
|
73
70
|
end
|
74
71
|
|
75
72
|
def log(command, &block)
|