mini_magick 4.9.5 → 5.0.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.
@@ -4,22 +4,6 @@ require 'logger'
4
4
  module MiniMagick
5
5
  module Configuration
6
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
7
  ##
24
8
  # Adds a prefix to the CLI command.
25
9
  # For example, you could use `firejail` to run all commands in a sandbox.
@@ -39,38 +23,19 @@ module MiniMagick
39
23
  #
40
24
  attr_accessor :timeout
41
25
  ##
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.
26
+ # Logger for commands, default is `Logger.new($stdout)`, but you can
27
+ # override it, for example if you want the logs to be written to a file.
52
28
  #
53
29
  # @return [Logger]
54
30
  #
55
31
  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
32
  ##
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`.
33
+ # Temporary directory used by MiniMagick, default is `Dir.tmpdir`, but
34
+ # you can override it.
70
35
  #
71
- # @return [Boolean]
36
+ # @return [String]
72
37
  #
73
- attr_accessor :validate_on_write
38
+ attr_accessor :tmpdir
74
39
 
75
40
  ##
76
41
  # If set to `false`, it will not raise errors when ImageMagick returns
@@ -78,121 +43,29 @@ module MiniMagick
78
43
  #
79
44
  # @return [Boolean]
80
45
  #
81
- attr_accessor :whiny
46
+ attr_accessor :errors
82
47
 
83
48
  ##
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
49
+ # If set to `false`, it will not forward warnings from ImageMagick to
50
+ # standard error.
51
+ attr_accessor :warnings
91
52
 
92
53
  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"
54
+ base.tmpdir = Dir.tmpdir
55
+ base.errors = true
97
56
  base.logger = Logger.new($stdout).tap { |l| l.level = Logger::INFO }
57
+ base.warnings = true
98
58
  end
99
59
 
100
60
  ##
101
61
  # @yield [self]
102
62
  # @example
103
63
  # MiniMagick.configure do |config|
104
- # config.cli = :graphicsmagick
105
64
  # config.timeout = 5
106
65
  # end
107
66
  #
108
67
  def configure
109
68
  yield self
110
69
  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
70
  end
198
71
  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
@@ -42,7 +38,7 @@ module MiniMagick
42
38
 
43
39
  def cheap_info(value)
44
40
  @info.fetch(value) do
45
- format, width, height, size = self["%m %w %h %b"].split(" ")
41
+ format, width, height, size = parse_warnings(self["%m %w %h %b"]).split(" ")
46
42
 
47
43
  path = @path
48
44
  path = path.match(/\[\d+\]$/).pre_match if path =~ /\[\d+\]$/
@@ -62,12 +58,18 @@ module MiniMagick
62
58
  raise MiniMagick::Invalid, "image data can't be read"
63
59
  end
64
60
 
65
- def colorspace
66
- @info["colorspace"] ||= self["%r"]
61
+ def parse_warnings(raw_info)
62
+ return raw_info unless raw_info.split("\n").size > 1
63
+
64
+ raw_info.split("\n").each do |line|
65
+ # must match "%m %w %h %b"
66
+ return line if line.match?(/^[A-Z]+ \d+ \d+ \d+(|\.\d+)([KMGTPEZY]{0,1})B$/)
67
+ end
68
+ raise TypeError
67
69
  end
68
70
 
69
- def mime_type
70
- "image/#{self["format"].downcase}"
71
+ def colorspace
72
+ @info["colorspace"] ||= self["%r"]
71
73
  end
72
74
 
73
75
  def resolution(unit = nil)
@@ -90,20 +92,12 @@ module MiniMagick
90
92
  output.each_line do |line|
91
93
  line = line.chomp("\n")
92
94
 
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
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)
106
98
  hash[key] = value
99
+ else
100
+ hash[hash.keys.last] << "\n#{line}"
107
101
  end
108
102
  end
109
103
 
@@ -119,44 +113,9 @@ module MiniMagick
119
113
  @info["signature"] ||= self["%#"]
120
114
  end
121
115
 
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
116
  def data
156
- raise Error, "MiniMagick::Image#data isn't supported on GraphicsMagick. Use MiniMagick::Image#details instead." if MiniMagick.graphicsmagick?
157
-
158
117
  @info["data"] ||= (
159
- json = MiniMagick::Tool::Convert.new do |convert|
118
+ json = MiniMagick.convert do |convert|
160
119
  convert << path
161
120
  convert << "json:"
162
121
  end
@@ -168,7 +127,7 @@ module MiniMagick
168
127
  end
169
128
 
170
129
  def identify
171
- MiniMagick::Tool::Identify.new do |builder|
130
+ MiniMagick.identify do |builder|
172
131
  yield builder if block_given?
173
132
  builder << path
174
133
  end
@@ -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 (BLOBBBB)
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.
@@ -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
- create(".dat", false) { |f| f.write(blob) }.tap do |image|
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::Tool::Convert.new do |convert|
58
+ MiniMagick.convert do |convert|
59
59
  convert.size "#{columns}x#{rows}"
60
60
  convert.depth depth
61
61
  convert << "#{map}:#{image.path}"
@@ -79,30 +79,16 @@ 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
- 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
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)
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) }
100
87
  else
101
- ext ||= File.extname(openable.to_s)
88
+ pathname = Pathname(path_or_url)
89
+ ext ||= File.extname(pathname.to_s)
90
+ pathname.open(binmode: true, **options) { |file| read(file, ext) }
102
91
  end
103
- ext.sub!(/:.*/, '') # hack for filenames or URLs that include a colon
104
-
105
- openable.open(options) { |file| read(file, ext) }
106
92
  end
107
93
 
108
94
  ##
@@ -114,18 +100,14 @@ module MiniMagick
114
100
  # we have a good tempfile.
115
101
  #
116
102
  # @param ext [String] Specify the extension you want to read it as
117
- # @param validate [Boolean] If false, skips validation of the created
118
- # image. Defaults to true.
119
103
  # @yield [Tempfile] You can #write bits to this object to create the new
120
104
  # Image
121
105
  # @return [MiniMagick::Image] The created image
122
106
  #
123
- def self.create(ext = nil, validate = MiniMagick.validate_on_create, &block)
107
+ def self.create(ext = nil, &block)
124
108
  tempfile = MiniMagick::Utilities.tempfile(ext.to_s.downcase, &block)
125
109
 
126
- new(tempfile.path, tempfile).tap do |image|
127
- image.validate! if validate
128
- end
110
+ new(tempfile.path, tempfile)
129
111
  end
130
112
 
131
113
  ##
@@ -160,7 +142,7 @@ module MiniMagick
160
142
  # which creates a temporary file for you and protects your original.
161
143
  #
162
144
  # @param input_path [String, Pathname] The location of an image file
163
- # @yield [MiniMagick::Tool::Mogrify] If block is given, {#combine_options}
145
+ # @yield [MiniMagick::Tool] If block is given, {#combine_options}
164
146
  # is called.
165
147
  #
166
148
  def initialize(input_path, tempfile = nil, &block)
@@ -224,10 +206,6 @@ module MiniMagick
224
206
  #
225
207
  attribute :type, "format"
226
208
  ##
227
- # @return [String]
228
- #
229
- attribute :mime_type
230
- ##
231
209
  # @return [Integer]
232
210
  #
233
211
  attribute :width
@@ -270,7 +248,7 @@ module MiniMagick
270
248
  #
271
249
  attribute :resolution
272
250
  ##
273
- # Returns the message digest of this image as a SHA-256, hexidecimal
251
+ # Returns the message digest of this image as a SHA-256, hexadecimal
274
252
  # encoded string. This signature uniquely identifies the image and is
275
253
  # convenient for determining if an image has been modified or whether two
276
254
  # images are identical.
@@ -282,17 +260,10 @@ module MiniMagick
282
260
  #
283
261
  attribute :signature
284
262
  ##
285
- # Returns the information from `identify -verbose` in a Hash format, for
286
- # ImageMagick.
263
+ # Returns the result of converting the image to JSON format.
287
264
  #
288
265
  # @return [Hash]
289
266
  attribute :data
290
- ##
291
- # Returns the information from `identify -verbose` in a Hash format, for
292
- # GraphicsMagick.
293
- #
294
- # @return [Hash]
295
- attribute :details
296
267
 
297
268
  ##
298
269
  # Use this method if you want to access raw Identify's format API.
@@ -336,13 +307,18 @@ module MiniMagick
336
307
  #
337
308
  # 1) one for each row of pixels
338
309
  # 2) one for each column of pixels
339
- # 3) three elements in the range 0-255, one for each of the RGB color channels
310
+ # 3) three or four elements in the range 0-255, one for each of the RGB(A) color channels
340
311
  #
341
312
  # @example
342
313
  # img = MiniMagick::Image.open 'image.jpg'
343
314
  # pixels = img.get_pixels
344
315
  # pixels[3][2][1] # the green channel value from the 4th-row, 3rd-column pixel
345
316
  #
317
+ # @example
318
+ # img = MiniMagick::Image.open 'image.jpg'
319
+ # pixels = img.get_pixels("RGBA")
320
+ # pixels[3][2][3] # the alpha channel value from the 4th-row, 3rd-column pixel
321
+ #
346
322
  # It can also be called after applying transformations:
347
323
  #
348
324
  # @example
@@ -353,19 +329,22 @@ module MiniMagick
353
329
  #
354
330
  # In this example, all pixels in pix should now have equal R, G, and B values.
355
331
  #
332
+ # @param map [String] A code for the mapping of the pixel data. Must be either
333
+ # 'RGB' or 'RGBA'. Default to 'RGB'
356
334
  # @return [Array] Matrix of each color of each pixel
357
- def get_pixels
358
- convert = MiniMagick::Tool::Convert.new
335
+ def get_pixels(map="RGB")
336
+ raise ArgumentError, "Invalid map value" unless ["RGB", "RGBA"].include?(map)
337
+ convert = MiniMagick.convert
359
338
  convert << path
360
339
  convert.depth(8)
361
- convert << "RGB:-"
340
+ convert << "#{map}:-"
362
341
 
363
342
  # Do not use `convert.call` here. We need the whole binary (unstripped) output here.
364
343
  shell = MiniMagick::Shell.new
365
344
  output, * = shell.run(convert.command)
366
345
 
367
346
  pixels_array = output.unpack("C*")
368
- pixels = pixels_array.each_slice(3).each_slice(width).to_a
347
+ pixels = pixels_array.each_slice(map.length).each_slice(width).to_a
369
348
 
370
349
  # deallocate large intermediary objects
371
350
  output.clear
@@ -374,6 +353,23 @@ module MiniMagick
374
353
  pixels
375
354
  end
376
355
 
356
+ ##
357
+ # This is used to create image from pixels. This might be required if you
358
+ # create pixels for some image processing reasons and you want to form
359
+ # image from those pixels.
360
+ #
361
+ # *DANGER*: This operation can be very expensive. Please try to use with
362
+ # caution.
363
+ #
364
+ # @example
365
+ # # It is given in readme.md file
366
+ ##
367
+ def self.get_image_from_pixels(pixels, dimension, map, depth, format)
368
+ pixels = pixels.flatten
369
+ blob = pixels.pack('C*')
370
+ import_pixels(blob, *dimension, depth, map, format)
371
+ end
372
+
377
373
  ##
378
374
  # This is used to change the format of the image. That is, from "tiff to
379
375
  # jpg" or something like that. Once you run it, the instance is pointing to
@@ -397,7 +393,7 @@ module MiniMagick
397
393
  # will convert all pages.
398
394
  # @param read_opts [Hash] Any read options to be passed to ImageMagick
399
395
  # for example: image.format('jpg', page, {density: '300'})
400
- # @yield [MiniMagick::Tool::Convert] It optionally yields the command,
396
+ # @yield [MiniMagick::Tool] It optionally yields the command,
401
397
  # if you want to add something.
402
398
  # @return [self]
403
399
  #
@@ -412,7 +408,7 @@ module MiniMagick
412
408
  input_path = path.dup
413
409
  input_path << "[#{page}]" if page && !layer?
414
410
 
415
- MiniMagick::Tool::Convert.new do |convert|
411
+ MiniMagick.convert do |convert|
416
412
  read_opts.each do |opt, val|
417
413
  convert.send(opt.to_s, val)
418
414
  end
@@ -432,6 +428,9 @@ module MiniMagick
432
428
  @info.clear
433
429
 
434
430
  self
431
+ rescue MiniMagick::Invalid, MiniMagick::Error => e
432
+ new_tempfile.unlink if new_tempfile && @tempfile != new_tempfile
433
+ raise e
435
434
  end
436
435
 
437
436
  ##
@@ -445,7 +444,7 @@ module MiniMagick
445
444
  # c.background "blue"
446
445
  # end
447
446
  #
448
- # @yield [MiniMagick::Tool::Mogrify]
447
+ # @yield [MiniMagick::Command]
449
448
  # @see http://www.imagemagick.org/script/mogrify.php
450
449
  # @return [self]
451
450
  #
@@ -466,10 +465,6 @@ module MiniMagick
466
465
  end
467
466
  end
468
467
 
469
- def respond_to_missing?(method_name, include_private = false)
470
- MiniMagick::Tool::Mogrify.option_methods.include?(method_name.to_s)
471
- end
472
-
473
468
  ##
474
469
  # Writes the temporary file out to either a file location (by passing in a
475
470
  # String) or by passing in a Stream that you can #write(chunk) to
@@ -482,7 +477,7 @@ module MiniMagick
482
477
  case output_to
483
478
  when String, Pathname
484
479
  if layer?
485
- MiniMagick::Tool::Convert.new do |builder|
480
+ MiniMagick.convert do |builder|
486
481
  builder << path
487
482
  builder << output_to
488
483
  end
@@ -492,6 +487,8 @@ module MiniMagick
492
487
  else
493
488
  IO.copy_stream File.open(path, "rb"), output_to
494
489
  end
490
+ ensure
491
+ destroy! if tempfile
495
492
  end
496
493
 
497
494
  ##
@@ -509,7 +506,7 @@ module MiniMagick
509
506
  def composite(other_image, output_extension = type.downcase, mask = nil)
510
507
  output_tempfile = MiniMagick::Utilities.tempfile(".#{output_extension}")
511
508
 
512
- MiniMagick::Tool::Composite.new do |composite|
509
+ MiniMagick.composite do |composite|
513
510
  yield composite if block_given?
514
511
  composite << other_image.path
515
512
  composite << path
@@ -551,27 +548,21 @@ module MiniMagick
551
548
  # b.verbose
552
549
  # end # runs `identify -verbose image.jpg`
553
550
  # @return [String] Output from `identify`
554
- # @yield [MiniMagick::Tool::Identify]
551
+ # @yield [MiniMagick::Tool]
555
552
  #
556
553
  def identify
557
- MiniMagick::Tool::Identify.new do |builder|
554
+ MiniMagick.identify do |builder|
558
555
  yield builder if block_given?
559
556
  builder << path
560
557
  end
561
558
  end
562
559
 
563
- # @private
564
- def run_command(tool_name, *args)
565
- MiniMagick::Tool.const_get(tool_name.capitalize).new do |builder|
566
- args.each do |arg|
567
- builder << arg
568
- end
569
- end
570
- end
571
-
572
560
  def mogrify(page = nil)
573
- MiniMagick::Tool::MogrifyRestricted.new do |builder|
561
+ MiniMagick.mogrify do |builder|
574
562
  yield builder if block_given?
563
+ if builder.args.include?("-format")
564
+ fail MiniMagick::Error, "you must call #format on a MiniMagick::Image directly"
565
+ end
575
566
  builder << (page ? "#{path}[#{page}]" : path)
576
567
  end
577
568
 
@@ -583,5 +574,31 @@ module MiniMagick
583
574
  def layer?
584
575
  path =~ /\[\d+\]$/
585
576
  end
577
+
578
+ ##
579
+ # Compares if image width
580
+ # is greater than height
581
+ # ============
582
+ # | |
583
+ # | |
584
+ # ============
585
+ # @return [Boolean]
586
+ def landscape?
587
+ width > height
588
+ end
589
+
590
+ ##
591
+ # Compares if image height
592
+ # is greater than width
593
+ # ======
594
+ # | |
595
+ # | |
596
+ # | |
597
+ # | |
598
+ # ======
599
+ # @return [Boolean]
600
+ def portrait?
601
+ height > width
602
+ end
586
603
  end
587
604
  end