mini_magick 3.7.0 → 4.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 095e582679936f6e2e1a42ea57b449867fa6eb75
4
- data.tar.gz: eb49ba1a91002faa50fc14d512e95668f55e398d
2
+ SHA256:
3
+ metadata.gz: f97cb0365a2a45cdb41c9091a4339fdd75a37cdb041c454cab800fd9464f5c0f
4
+ data.tar.gz: b5f97721ee2657631def0e8c498f257639e126a3ad745688356ae5a15ff5de9f
5
5
  SHA512:
6
- metadata.gz: 0de53204c54171eae9d42c7789b62b7e11733b1019c2994656c76ca2c43040268e47a1f6c30baa9f41675880ff36cf5e22f47a028a3a8d9a2260d0340b570c0c
7
- data.tar.gz: c03b97bb8c6be54b917afbc80ef3d738ced59c705ed209f3001c32cfe753bb4dc1e831397a1d0fb617b02cc95b9cb2d2a1574e2adc1334b90113e42d03556e4b
6
+ metadata.gz: 5339cf648a6cdc78f5a115e03ab77ec3f61bd83de5fcea0638ccb354e9b03b4c29e59b31e6681da9e1a238b2bee73b2a460ed5db0989eca90c097f29a9b0ebb5
7
+ data.tar.gz: 53b55f439ac4172e68e82c20e67a357160303fc3c382c0d7bb4bfeb4ba1e3e469b95ff89c517ab15f0c67436ab1c831fee5d7e88d0e981876cbc059b5c7dfad6
data/Rakefile CHANGED
@@ -6,13 +6,13 @@ $:.unshift 'lib'
6
6
  desc 'Default: run unit tests.'
7
7
  task :default => [:print_version, :spec]
8
8
 
9
- task :print_version do
9
+ task :print_version do
10
10
  puts `mogrify --version`
11
11
  end
12
12
 
13
13
  require 'rspec/core/rake_task'
14
14
 
15
- desc "Run specs"
15
+ desc 'Run specs'
16
16
  RSpec::Core::RakeTask.new do |t|
17
- t.pattern = "./spec/**/*_spec.rb"
17
+ t.pattern = './spec/**/*_spec.rb'
18
18
  end
data/lib/mini_gmagick.rb CHANGED
@@ -1,2 +1,3 @@
1
1
  require 'mini_magick'
2
+
2
3
  MiniMagick.processor = :gm
@@ -0,0 +1,190 @@
1
+ require 'mini_magick/utilities'
2
+ require 'logger'
3
+
4
+ module MiniMagick
5
+ module Configuration
6
+
7
+ ##
8
+ # If you don't have the CLI tools in your PATH, you can set the path to the
9
+ # executables.
10
+ #
11
+ attr_writer :cli_path
12
+ # @private (for backwards compatibility)
13
+ attr_accessor :processor_path
14
+
15
+ ##
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
+ #
21
+ # @return [String]
22
+ # @return [Array<String>]
23
+ #
24
+ attr_accessor :cli_prefix
25
+
26
+ ##
27
+ # If you don't want commands to take too long, you can set a timeout (in
28
+ # seconds).
29
+ #
30
+ # @return [Integer]
31
+ #
32
+ attr_accessor :timeout
33
+ ##
34
+ # When get to `true`, it outputs each command to STDOUT in their shell
35
+ # version.
36
+ #
37
+ # @return [Boolean]
38
+ #
39
+ attr_reader :debug
40
+ ##
41
+ # Logger for {#debug}, default is `MiniMagick::Logger.new(STDOUT)`, but
42
+ # you can override it, for example if you want the logs to be written to
43
+ # a file.
44
+ #
45
+ # @return [Logger]
46
+ #
47
+ attr_accessor :logger
48
+
49
+ ##
50
+ # If set to `true`, it will `identify` every newly created image, and raise
51
+ # `MiniMagick::Invalid` if the image is not valid. Useful for validating
52
+ # user input, although it adds a bit of overhead. Defaults to `true`.
53
+ #
54
+ # @return [Boolean]
55
+ #
56
+ attr_accessor :validate_on_create
57
+ ##
58
+ # If set to `true`, it will `identify` every image that gets written (with
59
+ # {MiniMagick::Image#write}), and raise `MiniMagick::Invalid` if the image
60
+ # is not valid. Useful for validating that processing was sucessful,
61
+ # although it adds a bit of overhead. Defaults to `true`.
62
+ #
63
+ # @return [Boolean]
64
+ #
65
+ attr_accessor :validate_on_write
66
+
67
+ ##
68
+ # If set to `false`, it will not raise errors when ImageMagick returns
69
+ # status code different than 0. Defaults to `true`.
70
+ #
71
+ # @return [Boolean]
72
+ #
73
+ attr_accessor :whiny
74
+
75
+ ##
76
+ # Instructs MiniMagick how to execute the shell commands. Available
77
+ # APIs are "open3" (default) and "posix-spawn" (requires the "posix-spawn"
78
+ # gem).
79
+ #
80
+ # @return [String]
81
+ #
82
+ attr_accessor :shell_api
83
+
84
+ def self.extended(base)
85
+ base.validate_on_create = true
86
+ base.validate_on_write = true
87
+ base.whiny = true
88
+ base.shell_api = "open3"
89
+ base.logger = Logger.new($stdout).tap { |l| l.level = Logger::INFO }
90
+ end
91
+
92
+ ##
93
+ # @yield [self]
94
+ # @example
95
+ # MiniMagick.configure do |config|
96
+ # config.cli = :graphicsmagick
97
+ # config.timeout = 5
98
+ # end
99
+ #
100
+ def configure
101
+ yield self
102
+ end
103
+
104
+ CLI_DETECTION = {
105
+ imagemagick7: "magick",
106
+ imagemagick: "mogrify",
107
+ graphicsmagick: "gm",
108
+ }
109
+
110
+ # @private (for backwards compatibility)
111
+ def processor
112
+ @processor ||= CLI_DETECTION.values.detect do |processor|
113
+ MiniMagick::Utilities.which(processor)
114
+ end
115
+ end
116
+
117
+ # @private (for backwards compatibility)
118
+ def processor=(processor)
119
+ @processor = processor.to_s
120
+
121
+ unless CLI_DETECTION.value?(@processor)
122
+ raise ArgumentError,
123
+ "processor has to be set to either \"magick\", \"mogrify\" or \"gm\"" \
124
+ ", was set to #{@processor.inspect}"
125
+ end
126
+ end
127
+
128
+ ##
129
+ # Get [ImageMagick](http://www.imagemagick.org) or
130
+ # [GraphicsMagick](http://www.graphicsmagick.org).
131
+ #
132
+ # @return [Symbol] `:imagemagick` or `:graphicsmagick`
133
+ #
134
+ def cli
135
+ if instance_variable_defined?("@cli")
136
+ instance_variable_get("@cli")
137
+ else
138
+ cli = CLI_DETECTION.key(processor) or
139
+ fail MiniMagick::Error, "You must have ImageMagick or GraphicsMagick installed"
140
+
141
+ instance_variable_set("@cli", cli)
142
+ end
143
+ end
144
+
145
+ ##
146
+ # Set whether you want to use [ImageMagick](http://www.imagemagick.org) or
147
+ # [GraphicsMagick](http://www.graphicsmagick.org).
148
+ #
149
+ def cli=(value)
150
+ @cli = value
151
+
152
+ if not CLI_DETECTION.key?(@cli)
153
+ raise ArgumentError,
154
+ "CLI has to be set to either :imagemagick, :imagemagick7 or :graphicsmagick" \
155
+ ", was set to #{@cli.inspect}"
156
+ end
157
+ end
158
+
159
+ ##
160
+ # If you set the path of CLI tools, you can get the path of the
161
+ # executables.
162
+ #
163
+ # @return [String]
164
+ #
165
+ def cli_path
166
+ if instance_variable_defined?("@cli_path")
167
+ instance_variable_get("@cli_path")
168
+ else
169
+ processor_path = instance_variable_get("@processor_path") if instance_variable_defined?("@processor_path")
170
+
171
+ instance_variable_set("@cli_path", processor_path)
172
+ end
173
+ end
174
+
175
+ ##
176
+ # When set to `true`, it outputs each command to STDOUT in their shell
177
+ # version.
178
+ #
179
+ def debug=(value)
180
+ warn "MiniMagick.debug is deprecated and will be removed in MiniMagick 5. Use `MiniMagick.logger.level = Logger::DEBUG` instead."
181
+ logger.level = value ? Logger::DEBUG : Logger::INFO
182
+ end
183
+
184
+ # Backwards compatibility
185
+ def reload_tools
186
+ warn "MiniMagick.reload_tools is deprecated because it is no longer necessary"
187
+ end
188
+
189
+ end
190
+ end
@@ -0,0 +1,202 @@
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 = parse_warnings(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 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+B$/
71
+ end
72
+ raise TypeError
73
+ end
74
+
75
+ def colorspace
76
+ @info["colorspace"] ||= self["%r"]
77
+ end
78
+
79
+ def mime_type
80
+ "image/#{self["format"].downcase}"
81
+ end
82
+
83
+ def resolution(unit = nil)
84
+ output = identify do |b|
85
+ b.units unit if unit
86
+ b.format "%x %y"
87
+ end
88
+ output.split(" ").map(&:to_i)
89
+ end
90
+
91
+ def raw_exif(value)
92
+ self["%[#{value}]"]
93
+ end
94
+
95
+ def exif
96
+ @info["exif"] ||= (
97
+ hash = {}
98
+ output = self["%[EXIF:*]"]
99
+
100
+ output.each_line do |line|
101
+ line = line.chomp("\n")
102
+
103
+ case MiniMagick.cli
104
+ when :imagemagick, :imagemagick7
105
+ if match = line.match(/^exif:/)
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
116
+ hash[key] = value
117
+ end
118
+ end
119
+
120
+ hash
121
+ )
122
+ end
123
+
124
+ def raw(value)
125
+ @info["raw:#{value}"] ||= identify { |b| b.format(value) }
126
+ end
127
+
128
+ def signature
129
+ @info["signature"] ||= self["%#"]
130
+ end
131
+
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
+ def data
166
+ raise Error, "MiniMagick::Image#data isn't supported on GraphicsMagick. Use MiniMagick::Image#details instead." if MiniMagick.graphicsmagick?
167
+
168
+ @info["data"] ||= (
169
+ json = MiniMagick::Tool::Convert.new do |convert|
170
+ convert << path
171
+ convert << "json:"
172
+ end
173
+
174
+ data = JSON.parse(json)
175
+ data = data.fetch(0) if data.is_a?(Array)
176
+ data.fetch("image")
177
+ )
178
+ end
179
+
180
+ def identify
181
+ MiniMagick::Tool::Identify.new do |builder|
182
+ yield builder if block_given?
183
+ builder << path
184
+ end
185
+ end
186
+
187
+ private
188
+
189
+ def decode_comma_separated_ascii_characters(encoded_value)
190
+ return encoded_value unless encoded_value.include?(',')
191
+ encoded_value.scan(/\d+/).map(&:to_i).map(&:chr).join
192
+ end
193
+
194
+ def path
195
+ value = @path
196
+ value += "[0]" unless value =~ /\[\d+\]$/
197
+ value
198
+ end
199
+
200
+ end
201
+ end
202
+ end