insano_image_resizer 0.4.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,16 +1,15 @@
1
1
  Insano Image Resizer
2
2
  ====================
3
3
 
4
- The Insano image resizer allows you to create resized versions of images, specifying a
4
+ The Insano image resizer allows you to create resized versions of images, specifying a
5
5
  desired width and height as well as a point of interest that the resizer will keep centered
6
- in the frame when possible. The resizer is built on top of the VIPS command-line tool,
7
- and is designed to be as fast as possible. In our tests, VIPS is faster than ImageMagick for any
8
- image larger than ~300x300px, and is exponentially faster for very large images.
6
+ in the frame when possible. The resizer is built on top of the VIPS command-line tool,
7
+ and is designed to be as fast as possible. In our tests, VIPS is faster than ImageMagick for any
8
+ image larger than ~300x300px, and is exponentially faster for very large images.
9
9
 
10
10
  ![A brief overview of the Insano processing function](insano_image_resizer/raw/master/samples/explanation.png)
11
11
 
12
- Output formats: The Insano image resizer will produce either a PNG or JPG image, depending
13
- on whether the source image includes transparency.
12
+ Output formats: The Insano image resizer will keep PNGs as PNG, but any other format is converted to JPEG
14
13
 
15
14
  * Insano is the fastest waterslide in the world. This isn't a waterslide, but it's similarly fast.
16
15
 
@@ -27,18 +26,18 @@ Example:
27
26
  input_path = 'samples/test.jpg'
28
27
 
29
28
  # Create a new instance of the Image processor
30
- processor = ImageResizer::Processor.new
31
-
29
+ processor = ImageResizer::Processor.new(:vips_path => 'path_to_vips[defaults to vips]', :identify_path => 'path_to_ImageMagick_identify[defaults to identify]')
30
+
32
31
  # Process the image, creating a temporary file.
33
32
  output_path = processor.process(input_path, {w: 100, h: 200}, {x:986, y:820, region: 0.5})
34
-
33
+
35
34
  # Move the image to an output path
36
35
  FileUtils.mv(output_path, 'samples/output/test.jpg')
37
36
 
38
37
  Input parameters:
39
38
 
40
39
  The `process` method is the main function of the Insano gem. Using different parameters,
41
- you can produce a wide range of resized images. Each of the parameters is explained below.
40
+ you can produce a wide range of resized images. Each of the parameters is explained below.
42
41
 
43
42
  The first argument is an input file path. Because the Insano image resizer uses the VIPS
44
43
  command line, it is not possible to transform an image that has been loaded into memory.
@@ -46,32 +45,31 @@ command line, it is not possible to transform an image that has been loaded into
46
45
  The second argument is a viewport hash containing width and height keys.
47
46
  You can specify both width and height to produce an output image of a specific size, or provide
48
47
  only width or height to have the resizer compute the other dimension based
49
- on the aspect ratio of the image. Finally, you can pass an empty hash to use
48
+ on the aspect ratio of the image. Finally, you can pass an empty hash to use
50
49
  the current width and height of the image. Note that the image resizer will
51
50
  never distort an image: the output image will always fill the viewport you provide,
52
51
  scaling up only if absolutely necessary.
53
52
 
54
53
  The third parameter is the point of interest that you'd like to keep centered if possible.
55
- Imagine that an 4:3 image contains a person's face on the left side. When you create a
54
+ Imagine that an 4:3 image contains a person's face on the left side. When you create a
56
55
  square thumbnail of the image, the persons face is half chopped off, because the processor
57
56
  trims off the left and right uniformly. Specifying a point of interest allows you to correct
58
- for this problem.
57
+ for this problem.
59
58
 
60
- By default, the POI is used only when cropping the image and deciding which sides
61
- should be cropped off. However, specifying the optional :region parameter with a value
62
- less than 1, you can make the image resizer zoom in around the POI, cropping the image
63
- so that an area of size (region * image size) around the POI is visible.
59
+ By default, the POI is used only when cropping the image and deciding which sides
60
+ should be cropped off. However, specifying the optional :region parameter with a value
61
+ less than 1, you can make the image resizer zoom in around the POI, cropping the image
62
+ so that an area of size (region * image size) around the POI is visible.
64
63
 
65
64
  Note that the output image may show more of the source image than you specify using the
66
65
  interest region. The region is only meant to indicate what region you'd like to ensure makes
67
- it into the output. For example, if you have a 200 x 200px image and request an output image of
66
+ it into the output. For example, if you have a 200 x 200px image and request an output image of
68
67
  100 x 100px, showing the 50px region around 150px x 150px, the output will contain more than
69
- just that region, since filling a 100px square with a 50px region would require enlarging the
68
+ just that region, since filling a 100px square with a 50px region would require enlarging the
70
69
  source image.
71
70
 
72
71
  Credits
73
72
  =======
74
73
 
75
- This project is loosely based off of the ImageResizer gem previously authored by Daniel Nelson of Populr.me.
76
- It draws heavily on the VIPS im_affinei command line function to resize images using an affine transform.
74
+ This project draws heavily on the VIPS im_affinei command line function to resize images using an affine transform.
77
75
 
@@ -39,15 +39,15 @@ module InsanoImageResizer
39
39
  def has_config_method?(method_name)
40
40
  config_methods.include?(method_name.to_sym)
41
41
  end
42
-
42
+
43
43
  def configuration
44
44
  @configuration ||= {}
45
45
  end
46
-
46
+
47
47
  def config_methods
48
48
  @config_methods ||= self.class.config_methods.dup
49
49
  end
50
-
50
+
51
51
  def default_configuration
52
52
  @default_configuration ||= self.class.default_configuration.dup
53
53
  end
@@ -57,7 +57,7 @@ module InsanoImageResizer
57
57
  child_configurables.each{|c| c.set_if_unset(key, value) }
58
58
  value
59
59
  end
60
-
60
+
61
61
  def use_as_fallback_config(other_configurable)
62
62
  other_configurable.add_child_configurable(self)
63
63
  self.fallback_configurable = other_configurable
@@ -76,17 +76,17 @@ module InsanoImageResizer
76
76
  end
77
77
 
78
78
  private
79
-
79
+
80
80
  attr_accessor :fallback_configurable
81
-
81
+
82
82
  def child_configurables
83
83
  @child_configurables ||= []
84
84
  end
85
-
85
+
86
86
  def set_locally?(key)
87
87
  instance_variable_defined?("@#{key}")
88
88
  end
89
-
89
+
90
90
  def default_value(key)
91
91
  if default_configuration[key].is_a?(DeferredBlock)
92
92
  default_configuration[key] = default_configuration[key].call
@@ -129,7 +129,7 @@ module InsanoImageResizer
129
129
  @nested_configurables ||= []
130
130
  end
131
131
 
132
- def register_configuration(name, config=nil, &config_in_block)
132
+ def register_configuration(name, config=nil, &config_in_block)
133
133
  saved_configs[name] = config_in_block || config
134
134
  end
135
135
 
@@ -164,7 +164,7 @@ module InsanoImageResizer
164
164
  def configuration_method(*method_names)
165
165
  config_methods.push(*method_names.map{|n| n.to_sym }).uniq!
166
166
  end
167
-
167
+
168
168
  def nested_configurable(*method_names)
169
169
  nested_configurables.push(*method_names)
170
170
  end
@@ -1,66 +1,49 @@
1
1
  require 'yaml'
2
- require 'shellwords'
3
2
  require 'exifr'
3
+ require 'cocaine'
4
4
 
5
5
  module InsanoImageResizer
6
6
  class Processor
7
7
 
8
8
  include Configurable
9
- include Shell
10
9
  include Loggable
10
+ include Cocaine
11
11
 
12
- def initialize(options = {vips_path: "vips"})
13
- @vips_path = options[:vips_path]
12
+ def initialize(options = {})
13
+ @vips_path = options[:vips_path] || 'vips'
14
+ @identify_path = options[:identify_path] || 'identify'
14
15
  end
15
16
 
16
17
  def process(input_path, viewport_size = {}, interest_point = {}, quality = 60)
17
- input_properties = fetch_image_properties(input_path)
18
- input_has_alpha = (input_properties[:bands] == 4)
18
+ width, height, original_format, target_extension = fetch_image_properties(input_path)
19
19
 
20
- output_tmp = Tempfile.new(["img", input_has_alpha ? ".png" : ".jpg"])
20
+ output_tmp = Tempfile.new(['img', ".#{target_extension}"])
21
21
 
22
- transform = calculate_transform(input_path, input_properties, viewport_size, interest_point)
23
- run_transform(input_path, output_tmp.path, transform, quality)
22
+ transform = calculate_transform(input_path, width, height, viewport_size, interest_point)
23
+ run_transform(input_path, output_tmp.path, transform, original_format, target_extension, quality)
24
24
 
25
- return output_tmp.path
25
+ output_tmp.path
26
26
  end
27
27
 
28
- def fetch_image_properties(input_path)
29
- # read in the image headers to discover the width and height of the image.
30
- # There's actually some extra metadata we ignore here, but this seems to be
31
- # the only way to get width and height from VIPS.
32
- result = {}
33
- result[:w] = run("#{@vips_path} im_header_int Xsize '#{input_path}'").to_f
34
- result[:h] = run("#{@vips_path} im_header_int Ysize '#{input_path}'").to_f
35
- result[:bands] = run("#{@vips_path} im_header_int Bands '#{input_path}'").to_f
36
-
37
- # find the EXIF values
38
- orientation = 0
39
- begin
40
- orientation = EXIFR::JPEG.new(input_path).orientation.to_i
41
- rescue
42
- orientation = 0
43
- end
28
+ private
44
29
 
45
- if orientation == 6 || orientation == 8
46
- nh = result[:w]
47
- result[:w] = result[:h]
48
- result[:h] = nh
49
- end
50
- return result
30
+ def fetch_image_properties(input_path)
31
+ line = Cocaine::CommandLine.new(@identify_path, '-format "%w %h %m" :input')
32
+ parts = line.run(:input => input_path).split(' ')
33
+ [parts[0].to_i, parts[1].to_i, parts[2], parts[2] == 'PNG' ? 'png' : 'jpg']
51
34
  end
52
35
 
53
- def calculate_transform(input_path, input_properties, viewport_size, interest_point)
36
+ def calculate_transform(input_path, width, height, viewport_size, interest_point)
54
37
 
55
38
  # By default, the interest size is 30% of the total image size.
56
39
  # In the future, this could be a parameter, and you'd pass the # of pixels around
57
40
  # the POI you are interested in.
58
41
  if (interest_point[:xf])
59
- interest_point[:x] = input_properties[:w] * interest_point[:xf]
42
+ interest_point[:x] = width * interest_point[:xf]
60
43
  end
61
44
 
62
45
  if (interest_point[:yf])
63
- interest_point[:y] = input_properties[:h] * interest_point[:yf]
46
+ interest_point[:y] = height * interest_point[:yf]
64
47
  end
65
48
 
66
49
  if (interest_point[:region] == nil)
@@ -68,27 +51,27 @@ module InsanoImageResizer
68
51
  end
69
52
 
70
53
  if (interest_point[:x] == nil)
71
- interest_point[:x] = input_properties[:w] * 0.5
54
+ interest_point[:x] = width * 0.5
72
55
  interest_point[:region] = 1
73
56
  end
74
57
  if (interest_point[:y] == nil)
75
- interest_point[:y] = input_properties[:h] * 0.5
58
+ interest_point[:y] = height * 0.5
76
59
  interest_point[:region] = 1
77
60
  end
78
61
 
79
- interest_size = {w: input_properties[:w] * interest_point[:region], h: input_properties[:h] * interest_point[:region]}
62
+ interest_size = {w: width * interest_point[:region], h: height * interest_point[:region]}
80
63
 
81
64
  # Has the user specified both the width and the height of the viewport? If they haven't,
82
65
  # let's go ahead and fill in the missing properties for them so that they get output at
83
66
  # the original aspect ratio of the image.
84
67
  if ((viewport_size[:w] == nil) && (viewport_size[:h] == nil))
85
- viewport_size = {w: input_properties[:w], h: input_properties[:h]}
68
+ viewport_size = {w: width, h: height}
86
69
 
87
70
  elsif (viewport_size[:w] == nil)
88
- viewport_size[:w] = (viewport_size[:h] * (input_properties[:w].to_f / input_properties[:h].to_f))
71
+ viewport_size[:w] = (viewport_size[:h] * (width.to_f / height.to_f))
89
72
 
90
73
  elsif (viewport_size[:h] == nil)
91
- viewport_size[:h] = (viewport_size[:w] * (input_properties[:h].to_f / input_properties[:w].to_f))
74
+ viewport_size[:h] = (viewport_size[:w] * (height.to_f / width.to_f))
92
75
  end
93
76
 
94
77
  # how can we take our current image and fit it into the viewport? Time for
@@ -100,28 +83,18 @@ module InsanoImageResizer
100
83
  # we won't get a simple scale-to-fill. We'll get a more zoomed-in version
101
84
  # showing just the 1/3 around the interest_point.
102
85
 
103
- scale_to_fill = [viewport_size[:w] / input_properties[:w].to_f, viewport_size[:h] / input_properties[:h].to_f].max
104
- scale_to_interest = [interest_size[:w] / input_properties[:w].to_f, interest_size[:h] / input_properties[:h].to_f].max
105
-
106
- log.debug("POI: ")
107
- log.debug(interest_point)
108
- log.debug("Image properties: ")
109
- log.debug(input_properties)
110
- log.debug("Requested viewport size: ")
111
- log.debug(viewport_size)
112
- log.debug("scale_to_fill: %f" % scale_to_fill)
113
- log.debug("scale_to_interest: %f" % scale_to_interest)
114
-
86
+ scale_to_fill = [viewport_size[:w] / width.to_f, viewport_size[:h] / height.to_f].max
87
+ scale_to_interest = [interest_size[:w] / width.to_f, interest_size[:h] / height.to_f].max
115
88
 
116
89
  scale_for_best_region = [scale_to_fill, scale_to_interest].max
117
90
 
118
91
  # cool! Now, let's figure out what the content offset within the image should be.
119
92
  # We want to keep the point of interest in view whenever possible. First, let's
120
93
  # compute an optimal frame around the POI:
121
- best_region = {x: interest_point[:x].to_f - (input_properties[:w].to_f * scale_for_best_region) / 2,
122
- y: interest_point[:y].to_f - (input_properties[:h].to_f * scale_for_best_region) / 2,
123
- w: input_properties[:w].to_f * scale_for_best_region,
124
- h: input_properties[:h].to_f * scale_for_best_region}
94
+ best_region = {x: interest_point[:x].to_f - (width.to_f * scale_for_best_region) / 2,
95
+ y: interest_point[:y].to_f - (height.to_f * scale_for_best_region) / 2,
96
+ w: width.to_f * scale_for_best_region,
97
+ h: height.to_f * scale_for_best_region}
125
98
 
126
99
  # Up to this point, we've been using 'scale_for_best_region' to be the preferred scale of the image.
127
100
  # So, scale could be 1/3 if we want to show the area around the POI, or 1 if we're fitting a whole image
@@ -157,12 +130,12 @@ module InsanoImageResizer
157
130
 
158
131
  # alright—now our transform most likely extends beyond the bounds of the image
159
132
  # data. Let's add some constraints that push it within the bounds of the image.
160
- if (transform[:x] + transform[:w] > input_properties[:w].to_f * scale)
161
- transform[:x] = input_properties[:w].to_f * scale - transform[:w]
133
+ if (transform[:x] + transform[:w] > width.to_f * scale)
134
+ transform[:x] = width.to_f * scale - transform[:w]
162
135
  end
163
136
 
164
- if (transform[:y] + transform[:h] > input_properties[:h].to_f * scale)
165
- transform[:y] = input_properties[:h].to_f * scale - transform[:h]
137
+ if (transform[:y] + transform[:h] > height.to_f * scale)
138
+ transform[:y] = height.to_f * scale - transform[:h]
166
139
  end
167
140
 
168
141
  if (transform[:x] < 0)
@@ -173,47 +146,19 @@ module InsanoImageResizer
173
146
  transform[:y] = 0.0
174
147
  end
175
148
 
176
- log.debug("The transform properties:")
177
- log.debug(transform)
178
-
179
- return transform
149
+ transform
180
150
  end
181
151
 
182
- def run_transform(input_path, output_path, transform, quality = 90)
152
+ def run_transform(input_path, output_path, transform, original_format, output_extension, quality)
183
153
  # Call through to VIPS:
184
154
  # int im_affinei(in, out, interpolate, a, b, c, d, dx, dy, x, y, w, h)
185
155
  # The first six params are a transformation matrix. A and D are used for X and Y
186
156
  # scale, the other two are b = Y skew and c = X skew. TX and TY are translations
187
157
  # but don't seem to be used.
188
158
  # The last four params define a rect of the source image that is transformed.
189
- output_extension = output_path[-3..-1]
190
- quality_extension = ""
191
159
 
192
- if (output_extension == "jpg")
193
- quality_extension = ":#{quality}"
194
- end
195
-
196
- # find the EXIF values
197
- orientation = 0
198
- begin
199
- orientation = EXIFR::JPEG.new(input_path).orientation.to_i
200
- rescue
201
- orientation = 0
202
- end
203
-
204
- if orientation == 3 || orientation == 6 || orientation == 8
205
- o_transform = []
206
- if orientation == 3
207
- run("#{@vips_path} im_rot180 '#{input_path}' '#{output_path}#{quality_extension}'")
208
- elsif orientation == 6
209
- run("#{@vips_path} im_rot90 '#{input_path}' '#{output_path}#{quality_extension}'")
210
- elsif orientation == 8
211
- run("#{@vips_path} im_rot270 '#{input_path}' '#{output_path}#{quality_extension}'")
212
- end
213
- FileUtils.rm(input_path)
214
- run("mogrify -strip #{output_path}")
215
- FileUtils.mv(output_path, input_path)
216
- end
160
+ quality_extension = ''
161
+ quality_extension = ":#{quality}" if output_extension == 'jpg'
217
162
 
218
163
  if (transform[:scale] < 0.5)
219
164
  # If we're shrinking the image by more than a factor of two, let's do a two-pass operation. The reason we do this
@@ -236,16 +181,67 @@ module InsanoImageResizer
236
181
  end
237
182
  intermediate_path = input_path[0..-4]+"_shrunk." + output_extension
238
183
 
239
- run("#{@vips_path} im_shrink '#{input_path}' '#{intermediate_path}#{quality_extension}' #{shrink_factor} #{shrink_factor}")
240
- run("#{@vips_path} im_affine '#{intermediate_path}' '#{output_path}#{quality_extension}' #{transform[:scale]} 0 0 #{transform[:scale]} 0 0 #{transform[:x]} #{transform[:y]} #{transform[:w]} #{transform[:h]}")
184
+ line = Cocaine::CommandLine.new(@vips_path, "im_shrink :input :intermediate_path :shrink_factor :shrink_factor")
185
+ line.run(:input => input_path,
186
+ :intermediate_path => "#{intermediate_path}#{quality_extension}",
187
+ :shrink_factor => shrink_factor.to_s)
188
+
189
+
190
+ line = Cocaine::CommandLine.new(@vips_path, "im_affine :intermediate_path :output :scale 0 0 :scale 0 0 :x :y :w :h")
191
+ line.run(:output => "#{output_path}#{quality_extension}",
192
+ :intermediate_path => intermediate_path,
193
+ :scale => transform[:scale].to_s,
194
+ :x => transform[:x].to_s,
195
+ :y => transform[:y].to_s,
196
+ :w => transform[:w].to_s,
197
+ :h => transform[:h].to_s)
198
+
241
199
  FileUtils.rm(intermediate_path)
242
200
 
243
201
  else
244
- run("#{@vips_path} im_affine '#{input_path}' '#{output_path}#{quality_extension}' #{transform[:scale]} 0 0 #{transform[:scale]} 0 0 #{transform[:x]} #{transform[:y]} #{transform[:w]} #{transform[:h]}")
202
+ line = Cocaine::CommandLine.new(@vips_path, "im_affine :input :output :scale 0 0 :scale 0 0 :x :y :w :h")
203
+ line.run(:output => "#{output_path}#{quality_extension}",
204
+ :input => input_path,
205
+ :scale => transform[:scale].to_s,
206
+ :x => transform[:x].to_s,
207
+ :y => transform[:y].to_s,
208
+ :w => transform[:w].to_s,
209
+ :h => transform[:h].to_s)
210
+ end
211
+
212
+ # find the EXIF values
213
+ orientation = 0
214
+ orientation = EXIFR::JPEG.new(input_path).orientation.to_i if original_format == 'JPEG'
215
+
216
+ if orientation == 3 || orientation == 6 || orientation == 8
217
+ FileUtils.mv(output_path, intermediate_path)
218
+ o_transform = []
219
+ if orientation == 3
220
+ command = 'im_rot180'
221
+ elsif orientation == 6
222
+ command = 'im_rot90'
223
+ elsif orientation == 8
224
+ command = 'im_rot270'
225
+ else
226
+ command = nil
227
+ end
228
+
229
+ if command
230
+ line = Cocaine::CommandLine.new(@vips_path, ":command :intermediate_path :output")
231
+ line.run(:output => "#{output_path}#{quality_extension}",
232
+ :intermediate_path => intermediate_path,
233
+ :command => command)
234
+ end
235
+
236
+ FileUtils.rm(intermediate_path)
237
+ line = Cocaine::CommandLine.new(mogrify, "-strip :output")
238
+ line.run(:output => output_path,
239
+ :intermediate_path => intermediate_path)
245
240
  end
246
241
 
247
242
  FileUtils.rm(input_path)
248
- return output_path
243
+
244
+ output_path
249
245
  end
250
246
  end
251
247
  end
@@ -1,5 +1,4 @@
1
1
 
2
2
  require 'insano_image_resizer/loggable'
3
3
  require 'insano_image_resizer/configurable'
4
- require 'insano_image_resizer/shell'
5
4
  require 'insano_image_resizer/processor'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: insano_image_resizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
4
+ version: 0.5.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,27 +9,33 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-27 00:00:00.000000000 Z
12
+ date: 2013-03-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: rspec
16
- requirement: !ruby/object:Gem::Requirement
15
+ name: cocaine
16
+ requirement: &2161837320 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
21
  version: '0'
22
- type: :development
22
+ type: :runtime
23
23
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
24
+ version_requirements: *2161837320
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2161836240 !ruby/object:Gem::Requirement
25
28
  none: false
26
29
  requirements:
27
30
  - - ! '>='
28
31
  - !ruby/object:Gem::Version
29
32
  version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2161836240
30
36
  - !ruby/object:Gem::Dependency
31
37
  name: jeweler
32
- requirement: !ruby/object:Gem::Requirement
38
+ requirement: &2161834680 !ruby/object:Gem::Requirement
33
39
  none: false
34
40
  requirements:
35
41
  - - ! '>='
@@ -37,15 +43,10 @@ dependencies:
37
43
  version: '0'
38
44
  type: :development
39
45
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: '0'
46
+ version_requirements: *2161834680
46
47
  - !ruby/object:Gem::Dependency
47
48
  name: pry
48
- requirement: !ruby/object:Gem::Requirement
49
+ requirement: &2161833680 !ruby/object:Gem::Requirement
49
50
  none: false
50
51
  requirements:
51
52
  - - ! '>='
@@ -53,15 +54,10 @@ dependencies:
53
54
  version: '0'
54
55
  type: :development
55
56
  prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
59
- - - ! '>='
60
- - !ruby/object:Gem::Version
61
- version: '0'
57
+ version_requirements: *2161833680
62
58
  - !ruby/object:Gem::Dependency
63
59
  name: pry-nav
64
- requirement: !ruby/object:Gem::Requirement
60
+ requirement: &2161832100 !ruby/object:Gem::Requirement
65
61
  none: false
66
62
  requirements:
67
63
  - - ! '>='
@@ -69,15 +65,10 @@ dependencies:
69
65
  version: '0'
70
66
  type: :development
71
67
  prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: '0'
68
+ version_requirements: *2161832100
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: pry-stack_explorer
80
- requirement: !ruby/object:Gem::Requirement
71
+ requirement: &2161830740 !ruby/object:Gem::Requirement
81
72
  none: false
82
73
  requirements:
83
74
  - - ! '>='
@@ -85,12 +76,18 @@ dependencies:
85
76
  version: '0'
86
77
  type: :development
87
78
  prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
79
+ version_requirements: *2161830740
80
+ - !ruby/object:Gem::Dependency
81
+ name: exifr
82
+ requirement: &2161845740 !ruby/object:Gem::Requirement
89
83
  none: false
90
84
  requirements:
91
85
  - - ! '>='
92
86
  - !ruby/object:Gem::Version
93
87
  version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2161845740
94
91
  description: ! "An image resizing gem that generates smaller versions of requested
95
92
  \n images. Calls through to VIPS on the command line to perform
96
93
  processing,\n and automatically handles cropping and scaling
@@ -107,7 +104,6 @@ files:
107
104
  - lib/insano_image_resizer/configurable.rb
108
105
  - lib/insano_image_resizer/loggable.rb
109
106
  - lib/insano_image_resizer/processor.rb
110
- - lib/insano_image_resizer/shell.rb
111
107
  - LICENSE
112
108
  - README.md
113
109
  homepage: http://github.com/populr/insano_image_resizer
@@ -123,6 +119,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
119
  - - ! '>='
124
120
  - !ruby/object:Gem::Version
125
121
  version: '0'
122
+ segments:
123
+ - 0
124
+ hash: -2229782920587942478
126
125
  required_rubygems_version: !ruby/object:Gem::Requirement
127
126
  none: false
128
127
  requirements:
@@ -131,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
130
  version: '0'
132
131
  requirements: []
133
132
  rubyforge_project:
134
- rubygems_version: 1.8.24
133
+ rubygems_version: 1.8.10
135
134
  signing_key:
136
135
  specification_version: 3
137
136
  summary: An image resizing gem that calls through to VIPS on the command line.
@@ -1,48 +0,0 @@
1
- require 'shellwords'
2
-
3
- module InsanoImageResizer
4
- module Shell
5
-
6
- include Configurable
7
- configurable_attr :log_commands, true
8
-
9
- # Exceptions
10
- class CommandFailed < RuntimeError; end
11
-
12
- def run(command, args="")
13
- full_command = "#{command} #{escape_args(args)}"
14
- log.debug("Running command: #{full_command}") if log_commands
15
- begin
16
- result = `#{full_command}`
17
- rescue Errno::ENOENT
18
- raise_shell_command_failed(full_command)
19
- end
20
- if $?.exitstatus == 1
21
- throw :unable_to_handle
22
- elsif !$?.success?
23
- raise_shell_command_failed(full_command)
24
- end
25
- result
26
- end
27
-
28
- def raise_shell_command_failed(command)
29
- raise CommandFailed, "Command failed (#{command}) with exit status #{$?.exitstatus}"
30
- end
31
-
32
- def escape_args(args)
33
- args.shellsplit.map do |arg|
34
- quote arg.gsub(/\\?'/, %q('\\\\''))
35
- end.join(' ')
36
- end
37
-
38
- def quote(string)
39
- q = running_on_windows? ? '"' : "'"
40
- q + string + q
41
- end
42
-
43
- def running_on_windows?
44
- ENV['OS'] && ENV['OS'].downcase == 'windows_nt'
45
- end
46
-
47
- end
48
- end