mini_magick 3.8.1 → 4.9.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/mini_gmagick.rb +1 -0
- data/lib/mini_magick/configuration.rb +198 -0
- data/lib/mini_magick/image/info.rb +192 -0
- data/lib/mini_magick/image.rb +456 -341
- data/lib/mini_magick/shell.rb +81 -0
- data/lib/mini_magick/tool/animate.rb +14 -0
- data/lib/mini_magick/tool/compare.rb +14 -0
- data/lib/mini_magick/tool/composite.rb +14 -0
- data/lib/mini_magick/tool/conjure.rb +14 -0
- data/lib/mini_magick/tool/convert.rb +14 -0
- data/lib/mini_magick/tool/display.rb +14 -0
- data/lib/mini_magick/tool/identify.rb +14 -0
- data/lib/mini_magick/tool/import.rb +14 -0
- data/lib/mini_magick/tool/magick.rb +14 -0
- data/lib/mini_magick/tool/mogrify.rb +14 -0
- data/lib/mini_magick/tool/mogrify_restricted.rb +15 -0
- data/lib/mini_magick/tool/montage.rb +14 -0
- data/lib/mini_magick/tool/stream.rb +14 -0
- data/lib/mini_magick/tool.rb +297 -0
- data/lib/mini_magick/utilities.rb +23 -50
- data/lib/mini_magick/version.rb +5 -5
- data/lib/mini_magick.rb +54 -65
- metadata +49 -51
- data/lib/mini_magick/command_builder.rb +0 -94
- data/lib/mini_magick/errors.rb +0 -4
- data/spec/files/actually_a_gif.jpg +0 -0
- data/spec/files/animation.gif +0 -0
- data/spec/files/composited.jpg +0 -0
- data/spec/files/erroneous.jpg +0 -0
- data/spec/files/layers.psd +0 -0
- data/spec/files/leaves (spaced).tiff +0 -0
- data/spec/files/not_an_image.php +0 -1
- data/spec/files/png.png +0 -0
- data/spec/files/simple-minus.gif +0 -0
- data/spec/files/simple.gif +0 -0
- data/spec/files/trogdor.jpg +0 -0
- data/spec/files/trogdor_capitalized.JPG +0 -0
- data/spec/lib/mini_magick/command_builder_spec.rb +0 -153
- data/spec/lib/mini_magick/image_spec.rb +0 -499
- data/spec/lib/mini_magick_spec.rb +0 -63
- data/spec/spec_helper.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3ab4ce03957b9a0f941f9bd2c34a5dcab63faffac5143eb6e7c90d7d860b57c5
|
4
|
+
data.tar.gz: 7bd78f0c862f1ca72b34e3c6fc4d14297adfa9dd6bc36218a35ef68c235320b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16c4bfa3744a2bea117bff0118afcbf89fac2f3d9e92c5c8af2b095f6c1381c1d000bede5086ccffd72ecca8152845cde11a5ff762186bd90793ee5d83e03a77
|
7
|
+
data.tar.gz: fe593ad2a7bfd4b6f2d9892533fd5347046e7dfb6cfd8e3bc6049cb1ae7686f5af60c23f33254175bca4728a58bdeeab2b9bfb873ebd94c46f14e68e086f002f
|
data/lib/mini_gmagick.rb
CHANGED
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'mini_magick/utilities'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module MiniMagick
|
5
|
+
module Configuration
|
6
|
+
|
7
|
+
##
|
8
|
+
# Set whether you want to use [ImageMagick](http://www.imagemagick.org) or
|
9
|
+
# [GraphicsMagick](http://www.graphicsmagick.org).
|
10
|
+
#
|
11
|
+
# @return [Symbol] `:imagemagick`, `:imagemagick7`, or `:graphicsmagick`
|
12
|
+
#
|
13
|
+
attr_accessor :cli
|
14
|
+
|
15
|
+
##
|
16
|
+
# If you don't have the CLI tools in your PATH, you can set the path to the
|
17
|
+
# executables.
|
18
|
+
#
|
19
|
+
attr_writer :cli_path
|
20
|
+
# @private (for backwards compatibility)
|
21
|
+
attr_accessor :processor_path
|
22
|
+
|
23
|
+
##
|
24
|
+
# Adds a prefix to the CLI command.
|
25
|
+
# For example, you could use `firejail` to run all commands in a sandbox.
|
26
|
+
# Can be a string, or an array of strings.
|
27
|
+
# e.g. 'firejail', or ['firejail', '--force']
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
# @return [Array<String>]
|
31
|
+
#
|
32
|
+
attr_accessor :cli_prefix
|
33
|
+
|
34
|
+
##
|
35
|
+
# If you don't want commands to take too long, you can set a timeout (in
|
36
|
+
# seconds).
|
37
|
+
#
|
38
|
+
# @return [Integer]
|
39
|
+
#
|
40
|
+
attr_accessor :timeout
|
41
|
+
##
|
42
|
+
# When get to `true`, it outputs each command to STDOUT in their shell
|
43
|
+
# version.
|
44
|
+
#
|
45
|
+
# @return [Boolean]
|
46
|
+
#
|
47
|
+
attr_reader :debug
|
48
|
+
##
|
49
|
+
# Logger for {#debug}, default is `MiniMagick::Logger.new(STDOUT)`, but
|
50
|
+
# you can override it, for example if you want the logs to be written to
|
51
|
+
# a file.
|
52
|
+
#
|
53
|
+
# @return [Logger]
|
54
|
+
#
|
55
|
+
attr_accessor :logger
|
56
|
+
|
57
|
+
##
|
58
|
+
# If set to `true`, it will `identify` every newly created image, and raise
|
59
|
+
# `MiniMagick::Invalid` if the image is not valid. Useful for validating
|
60
|
+
# user input, although it adds a bit of overhead. Defaults to `true`.
|
61
|
+
#
|
62
|
+
# @return [Boolean]
|
63
|
+
#
|
64
|
+
attr_accessor :validate_on_create
|
65
|
+
##
|
66
|
+
# If set to `true`, it will `identify` every image that gets written (with
|
67
|
+
# {MiniMagick::Image#write}), and raise `MiniMagick::Invalid` if the image
|
68
|
+
# is not valid. Useful for validating that processing was sucessful,
|
69
|
+
# although it adds a bit of overhead. Defaults to `true`.
|
70
|
+
#
|
71
|
+
# @return [Boolean]
|
72
|
+
#
|
73
|
+
attr_accessor :validate_on_write
|
74
|
+
|
75
|
+
##
|
76
|
+
# If set to `false`, it will not raise errors when ImageMagick returns
|
77
|
+
# status code different than 0. Defaults to `true`.
|
78
|
+
#
|
79
|
+
# @return [Boolean]
|
80
|
+
#
|
81
|
+
attr_accessor :whiny
|
82
|
+
|
83
|
+
##
|
84
|
+
# Instructs MiniMagick how to execute the shell commands. Available
|
85
|
+
# APIs are "open3" (default) and "posix-spawn" (requires the "posix-spawn"
|
86
|
+
# gem).
|
87
|
+
#
|
88
|
+
# @return [String]
|
89
|
+
#
|
90
|
+
attr_accessor :shell_api
|
91
|
+
|
92
|
+
def self.extended(base)
|
93
|
+
base.validate_on_create = true
|
94
|
+
base.validate_on_write = true
|
95
|
+
base.whiny = true
|
96
|
+
base.shell_api = "open3"
|
97
|
+
base.logger = Logger.new($stdout).tap { |l| l.level = Logger::INFO }
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# @yield [self]
|
102
|
+
# @example
|
103
|
+
# MiniMagick.configure do |config|
|
104
|
+
# config.cli = :graphicsmagick
|
105
|
+
# config.timeout = 5
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
def configure
|
109
|
+
yield self
|
110
|
+
end
|
111
|
+
|
112
|
+
CLI_DETECTION = {
|
113
|
+
imagemagick: "mogrify",
|
114
|
+
graphicsmagick: "gm",
|
115
|
+
imagemagick7: "magick",
|
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
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module MiniMagick
|
4
|
+
class Image
|
5
|
+
# @private
|
6
|
+
class Info
|
7
|
+
ASCII_ENCODED_EXIF_KEYS = %w[ExifVersion FlashPixVersion]
|
8
|
+
|
9
|
+
def initialize(path)
|
10
|
+
@path = path
|
11
|
+
@info = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](value, *args)
|
15
|
+
case value
|
16
|
+
when "format", "width", "height", "dimensions", "size", "human_size"
|
17
|
+
cheap_info(value)
|
18
|
+
when "colorspace"
|
19
|
+
colorspace
|
20
|
+
when "mime_type"
|
21
|
+
mime_type
|
22
|
+
when "resolution"
|
23
|
+
resolution(*args)
|
24
|
+
when "signature"
|
25
|
+
signature
|
26
|
+
when /^EXIF\:/i
|
27
|
+
raw_exif(value)
|
28
|
+
when "exif"
|
29
|
+
exif
|
30
|
+
when "details"
|
31
|
+
details
|
32
|
+
when "data"
|
33
|
+
data
|
34
|
+
else
|
35
|
+
raw(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def clear
|
40
|
+
@info.clear
|
41
|
+
end
|
42
|
+
|
43
|
+
def cheap_info(value)
|
44
|
+
@info.fetch(value) do
|
45
|
+
format, width, height, size = self["%m %w %h %b"].split(" ")
|
46
|
+
|
47
|
+
path = @path
|
48
|
+
path = path.match(/\[\d+\]$/).pre_match if path =~ /\[\d+\]$/
|
49
|
+
|
50
|
+
@info.update(
|
51
|
+
"format" => format,
|
52
|
+
"width" => Integer(width),
|
53
|
+
"height" => Integer(height),
|
54
|
+
"dimensions" => [Integer(width), Integer(height)],
|
55
|
+
"size" => File.size(path),
|
56
|
+
"human_size" => size,
|
57
|
+
)
|
58
|
+
|
59
|
+
@info.fetch(value)
|
60
|
+
end
|
61
|
+
rescue ArgumentError, TypeError
|
62
|
+
raise MiniMagick::Invalid, "image data can't be read"
|
63
|
+
end
|
64
|
+
|
65
|
+
def colorspace
|
66
|
+
@info["colorspace"] ||= self["%r"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def mime_type
|
70
|
+
"image/#{self["format"].downcase}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def resolution(unit = nil)
|
74
|
+
output = identify do |b|
|
75
|
+
b.units unit if unit
|
76
|
+
b.format "%x %y"
|
77
|
+
end
|
78
|
+
output.split(" ").map(&:to_i)
|
79
|
+
end
|
80
|
+
|
81
|
+
def raw_exif(value)
|
82
|
+
self["%[#{value}]"]
|
83
|
+
end
|
84
|
+
|
85
|
+
def exif
|
86
|
+
@info["exif"] ||= (
|
87
|
+
hash = {}
|
88
|
+
output = self["%[EXIF:*]"]
|
89
|
+
|
90
|
+
output.each_line do |line|
|
91
|
+
line = line.chomp("\n")
|
92
|
+
|
93
|
+
case MiniMagick.cli
|
94
|
+
when :imagemagick, :imagemagick7
|
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)
|
98
|
+
hash[key] = value
|
99
|
+
else
|
100
|
+
hash[hash.keys.last] << "\n#{line}"
|
101
|
+
end
|
102
|
+
when :graphicsmagick
|
103
|
+
next if line == "unknown"
|
104
|
+
key, value = line.split("=", 2)
|
105
|
+
value.gsub!("\\012", "\n") # convert "\012" characters to newlines
|
106
|
+
hash[key] = value
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
hash
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def raw(value)
|
115
|
+
@info["raw:#{value}"] ||= identify { |b| b.format(value) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def signature
|
119
|
+
@info["signature"] ||= self["%#"]
|
120
|
+
end
|
121
|
+
|
122
|
+
def details
|
123
|
+
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?
|
124
|
+
|
125
|
+
@info["details"] ||= (
|
126
|
+
details_string = identify(&:verbose)
|
127
|
+
key_stack = []
|
128
|
+
details_string.lines.to_a[1..-1].each_with_object({}) do |line, details_hash|
|
129
|
+
next if !line.valid_encoding? || line.strip.length.zero?
|
130
|
+
|
131
|
+
level = line[/^\s*/].length / 2 - 1
|
132
|
+
if level >= 0
|
133
|
+
key_stack.pop until key_stack.size <= level
|
134
|
+
else
|
135
|
+
# Some metadata, such as SVG clipping paths, will be saved without
|
136
|
+
# indentation, resulting in a level of -1
|
137
|
+
last_key = details_hash.keys.last
|
138
|
+
details_hash[last_key] = '' if details_hash[last_key].empty?
|
139
|
+
details_hash[last_key] << line
|
140
|
+
next
|
141
|
+
end
|
142
|
+
|
143
|
+
key, _, value = line.partition(/:[\s]/).map(&:strip)
|
144
|
+
hash = key_stack.inject(details_hash) { |_hash, _key| _hash.fetch(_key) }
|
145
|
+
if value.empty?
|
146
|
+
hash[key] = {}
|
147
|
+
key_stack.push key
|
148
|
+
else
|
149
|
+
hash[key] = value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
)
|
153
|
+
end
|
154
|
+
|
155
|
+
def data
|
156
|
+
raise Error, "MiniMagick::Image#data isn't supported on GraphicsMagick. Use MiniMagick::Image#details instead." if MiniMagick.graphicsmagick?
|
157
|
+
|
158
|
+
@info["data"] ||= (
|
159
|
+
json = MiniMagick::Tool::Convert.new do |convert|
|
160
|
+
convert << path
|
161
|
+
convert << "json:"
|
162
|
+
end
|
163
|
+
|
164
|
+
data = JSON.parse(json)
|
165
|
+
data = data.fetch(0) if data.is_a?(Array)
|
166
|
+
data.fetch("image")
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
def identify
|
171
|
+
MiniMagick::Tool::Identify.new do |builder|
|
172
|
+
yield builder if block_given?
|
173
|
+
builder << path
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def decode_comma_separated_ascii_characters(encoded_value)
|
180
|
+
return encoded_value unless encoded_value.include?(',')
|
181
|
+
encoded_value.scan(/\d+/).map(&:to_i).map(&:chr).join
|
182
|
+
end
|
183
|
+
|
184
|
+
def path
|
185
|
+
value = @path
|
186
|
+
value += "[0]" unless value =~ /\[\d+\]$/
|
187
|
+
value
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|