insano_image_resizer 0.4.6 → 0.5.7
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.
- 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
|

|
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
|