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 +19 -21
- data/lib/insano_image_resizer/configurable.rb +10 -10
- data/lib/insano_image_resizer/processor.rb +93 -97
- data/lib/insano_image_resizer.rb +0 -1
- metadata +30 -31
- data/lib/insano_image_resizer/shell.rb +0 -48
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
|
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
|
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 = {
|
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
|
-
|
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([
|
20
|
+
output_tmp = Tempfile.new(['img', ".#{target_extension}"])
|
21
21
|
|
22
|
-
transform = calculate_transform(input_path,
|
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
|
-
|
25
|
+
output_tmp.path
|
26
26
|
end
|
27
27
|
|
28
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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,
|
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] =
|
42
|
+
interest_point[:x] = width * interest_point[:xf]
|
60
43
|
end
|
61
44
|
|
62
45
|
if (interest_point[:yf])
|
63
|
-
interest_point[:y] =
|
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] =
|
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] =
|
58
|
+
interest_point[:y] = height * 0.5
|
76
59
|
interest_point[:region] = 1
|
77
60
|
end
|
78
61
|
|
79
|
-
interest_size = {w:
|
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:
|
68
|
+
viewport_size = {w: width, h: height}
|
86
69
|
|
87
70
|
elsif (viewport_size[:w] == nil)
|
88
|
-
viewport_size[:w] = (viewport_size[:h] * (
|
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] * (
|
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] /
|
104
|
-
scale_to_interest = [interest_size[:w] /
|
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 - (
|
122
|
-
y: interest_point[:y].to_f - (
|
123
|
-
w:
|
124
|
-
h:
|
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] >
|
161
|
-
transform[:x] =
|
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] >
|
165
|
-
transform[:y] =
|
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
|
-
|
177
|
-
log.debug(transform)
|
178
|
-
|
179
|
-
return transform
|
149
|
+
transform
|
180
150
|
end
|
181
151
|
|
182
|
-
def run_transform(input_path, output_path, transform,
|
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
|
-
|
193
|
-
|
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
|
-
|
240
|
-
run(
|
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
|
-
|
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
|
-
|
243
|
+
|
244
|
+
output_path
|
249
245
|
end
|
250
246
|
end
|
251
247
|
end
|
data/lib/insano_image_resizer.rb
CHANGED
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
|
+
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:
|
12
|
+
date: 2013-03-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
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: :
|
22
|
+
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
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:
|
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:
|
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:
|
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:
|
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.
|
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
|