mini_magick 3.7.0 → 4.11.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
- 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