rmagick-screwdrivers 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +11 -0
- data/.gitignore +17 -0
- data/Gemfile +10 -0
- data/LICENSE +20 -0
- data/README.md +51 -0
- data/Rakefile +23 -0
- data/bin/magick_collage +77 -0
- data/bin/magick_poster +109 -0
- data/bin/magick_scale +64 -0
- data/features/rmagick-screwdrivers.feature +47 -0
- data/features/step_definitions/rmagick-screwdrivers_steps.rb +64 -0
- data/features/support/env.rb +5 -0
- data/lib/rmagick/screwdrivers.rb +11 -0
- data/lib/rmagick/screwdrivers/collage.rb +46 -0
- data/lib/rmagick/screwdrivers/helpers.rb +41 -0
- data/lib/rmagick/screwdrivers/poster.rb +99 -0
- data/lib/rmagick/screwdrivers/scale.rb +66 -0
- data/lib/rmagick/screwdrivers/version.rb +5 -0
- data/rmagick-screwdrivers.gemspec +36 -0
- data/spec/rmagick-screwdrivers_spec.rb +7 -0
- data/spec/spec_helper.rb +11 -0
- metadata +153 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: f6996c1d9295fecc5d2593702cd00ccac473d47e
|
4
|
+
data.tar.gz: 1f900b09343ae483b734ade275a4e597be363f41
|
5
|
+
!binary "U0hBNTEy":
|
6
|
+
metadata.gz: f7ecfdf5c4f190b0e533a6f7ba78873006dd1577460d024db7d86dca6531a9b758e9e935628021935f7b0d8258f4ca1ef6a25c6123d961bede2fa2cd1187321e
|
7
|
+
data.tar.gz: 9fb72a166559f83c45f5c684c2111414031f54658fbfd114a3b36ff7006f43e2b88248a65cf03bf64bc69fc6f8eceb4dae63233f327067d85ffcc6d81d0c9d1d
|
data/.document
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# .document is used by rdoc and yard to know how to generate documentation
|
2
|
+
# for example, it can be used to control how rdoc gets built when you do `gem install foo`
|
3
|
+
|
4
|
+
README.rdoc
|
5
|
+
lib/**/*.rb
|
6
|
+
bin/*
|
7
|
+
|
8
|
+
# Files below this - are treated as 'extra files', and aren't parsed for ruby code
|
9
|
+
-
|
10
|
+
features/**/*.feature
|
11
|
+
LICENSE
|
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in rmagick-screwdrivers.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem "rspec-core", :git => 'git://github.com/rspec/rspec-core.git'
|
7
|
+
gem "rspec-expectations", :path => '../rspec-expectations'
|
8
|
+
gem "rspec-mocks", :git => 'git://github.com/rspec/rspec-mocks.git'
|
9
|
+
gem "rspec-support", :git => 'git://github.com/rspec/rspec-support.git'
|
10
|
+
gem "rspec", :git => 'git://github.com/rspec/rspec.git'
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Alexei Matyushkin
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Rmagick::Screwdrivers
|
2
|
+
|
3
|
+
Simple set of classes and their binary wrappers to make routine operations
|
4
|
+
with RMagick pleasant:
|
5
|
+
|
6
|
+
* **scale** — to scale an image to a set of scaled images with optional
|
7
|
+
watermark (text and/or date) applied
|
8
|
+
* method: `Magick::Screwdrivers.scale`
|
9
|
+
* binary: `bin/rmagick_scale`
|
10
|
+
|
11
|
+
* **collage** — to produce a collage of a directory with images
|
12
|
+
* method: `Magick::Screwdrivers.collage`
|
13
|
+
* binary: `bin/rmagick_collage`
|
14
|
+
|
15
|
+
* **poster** — to produce a poster from an image (a.k.a demotivator)
|
16
|
+
* method: `Magick::Screwdrivers.poster`
|
17
|
+
* binary: `bin/rmagick_poster`
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
gem 'rmagick-screwdrivers'
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or simply install the gem for binary usage:
|
30
|
+
|
31
|
+
gem install rmagick-screwdrivers
|
32
|
+
|
33
|
+
and make heavy use of it:
|
34
|
+
|
35
|
+
$ magick_collage --help
|
36
|
+
$ magick_poster -v --font DejaVuSans --type classic ~/img/img1.jpg 'Hello,' 'I’m a poster'
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
$ magick_poster --help
|
41
|
+
$ magick_scale --help
|
42
|
+
$ magick_collage --help
|
43
|
+
|
44
|
+
## Contributing
|
45
|
+
|
46
|
+
1. Fork it
|
47
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
48
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
49
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
50
|
+
5. Create new Pull Request
|
51
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
require 'bueller'
|
4
|
+
Bueller::Tasks.new
|
5
|
+
Bueller::GemcutterTasks.new
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
RSpec::Core::RakeTask.new(:examples) do |examples|
|
9
|
+
examples.rspec_opts = '-Ispec'
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
13
|
+
spec.rspec_opts = '-Ispec'
|
14
|
+
spec.rcov = true
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'cucumber/rake/task'
|
18
|
+
Cucumber::Rake::Task.new(:features)
|
19
|
+
|
20
|
+
task :default => :examples
|
21
|
+
|
22
|
+
require 'yard'
|
23
|
+
YARD::Rake::YardocTask.new
|
data/bin/magick_collage
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
6
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
7
|
+
|
8
|
+
require 'rmagick/screwdrivers'
|
9
|
+
|
10
|
+
# options = {
|
11
|
+
# :columns => 5,
|
12
|
+
# :scale_range => 0.1,
|
13
|
+
# :thumb_width => 120,
|
14
|
+
# :rotate_angle => 20,
|
15
|
+
# :background => 'white',
|
16
|
+
# :border => '#DDDDDD',
|
17
|
+
# :logger => nil
|
18
|
+
# }.merge(options)
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
OptionParser.new do |opts|
|
22
|
+
opts.banner = "Usage: #{$0} [options] FILE|DIR [DIR]"
|
23
|
+
|
24
|
+
# Verbose?
|
25
|
+
opts.on("-v", "--verbose", "Run verbosely (default: false)") do |v|
|
26
|
+
if v
|
27
|
+
require 'logger'
|
28
|
+
options[:logger] = Logger.new STDOUT
|
29
|
+
end
|
30
|
+
end
|
31
|
+
# Quality
|
32
|
+
opts.on("-q", "--quality [1-100]", Integer, "Quality of resulting JPEG (default: 90)") do |quality|
|
33
|
+
options[:quality] = quality
|
34
|
+
end
|
35
|
+
# Columns
|
36
|
+
opts.on("-c", "--columns COLS", Integer, "Number of columns in collage (default: 5)") do |cols|
|
37
|
+
options[:columns] = cols
|
38
|
+
end
|
39
|
+
# Maximal ± of thumbnail size (random(-s,+s))
|
40
|
+
opts.on("-s", "--scale-range VAL", Float, "Maximal ± of standard thumbnail size (default: 0.1)") do |scale_range|
|
41
|
+
options[:scale_range] = scale_range
|
42
|
+
end
|
43
|
+
# Thumbnail standard width
|
44
|
+
opts.on("-t", "--thumbsize SIZE", "Thumbnail standard width (default: 120)") do |thumb_width|
|
45
|
+
options[:thumb_width] = thumb_width
|
46
|
+
end
|
47
|
+
# Maximal angle to rotate thumbnails
|
48
|
+
opts.on("-a", "--rotate-angle ANGLE", "Maximal angle to rotate thumbnails (default: 20°)") do |rotate_angle|
|
49
|
+
options[:rotate_angle] = rotate_angle
|
50
|
+
end
|
51
|
+
# Background color
|
52
|
+
opts.on("-b", "--background COLOR", "Background color (default: 'white')") do |background|
|
53
|
+
options[:background] = background
|
54
|
+
end
|
55
|
+
# Foreground color
|
56
|
+
opts.on("-f", "--foreground COLOR", "Border color (default: '#DDDDDD')") do |foreground|
|
57
|
+
options[:border] = foreground
|
58
|
+
end
|
59
|
+
|
60
|
+
# No argument, shows at tail. This will print an options summary.
|
61
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
62
|
+
puts opts
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
|
66
|
+
end.parse!
|
67
|
+
|
68
|
+
raise "Run `#{$0} --help` for execution examples. Exiting…" if ARGV.size.zero? || ARGV.size > 2
|
69
|
+
|
70
|
+
file = ARGV.first
|
71
|
+
dir = ARGV.size == 2 ? ARGV.last : File.dirname(file)
|
72
|
+
`mkdir -p #{dir}`
|
73
|
+
|
74
|
+
outfile = File.basename(file).sub(/$/, "-collage.jpg")
|
75
|
+
Magick::Screwdrivers::collage(file, options).write(File.join dir, outfile) {
|
76
|
+
self.quality = options[:quality] || 90
|
77
|
+
}
|
data/bin/magick_poster
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
6
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
7
|
+
|
8
|
+
require 'rmagick/screwdrivers'
|
9
|
+
|
10
|
+
# options = {
|
11
|
+
# :color => '#FFFFFF',
|
12
|
+
# :stroke => nil,
|
13
|
+
# :width => 600,
|
14
|
+
# :type => :classic, # :classic for black square around
|
15
|
+
# :lineheight => 6,
|
16
|
+
# :background => '#000000',
|
17
|
+
# :font => '/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf',
|
18
|
+
# :logger => nil
|
19
|
+
# }.merge(options)
|
20
|
+
|
21
|
+
options = {}
|
22
|
+
FONT_PATH = "/usr/share/fonts/truetype/*/**"
|
23
|
+
OptionParser.new do |opts|
|
24
|
+
opts.banner = "Usage: #{$0} [options] FILE TEXT1 TEXT2 [DIR]"
|
25
|
+
|
26
|
+
# Verbose?
|
27
|
+
opts.on("-v", "--verbose", "Run verbosely (default: false)") do |v|
|
28
|
+
if v
|
29
|
+
require 'logger'
|
30
|
+
options[:logger] = Logger.new STDOUT
|
31
|
+
end
|
32
|
+
end
|
33
|
+
# Quality
|
34
|
+
opts.on("-q", "--quality [1-100]", Integer, "Quality of resulting JPEG (default: 90)") do |quality|
|
35
|
+
options[:quality] = quality
|
36
|
+
end
|
37
|
+
# Color
|
38
|
+
opts.on("-c", "--color CLR", "Color of title (default: '#FFFFFF')") do |color|
|
39
|
+
options[:color] = color
|
40
|
+
end
|
41
|
+
# Stroke
|
42
|
+
opts.on("-s", "--stroke CLR", "Stroke color (default: nil)") do |stroke|
|
43
|
+
options[:stroke] = stroke
|
44
|
+
end
|
45
|
+
# Width
|
46
|
+
opts.on("-w", "--width PIXELS", Integer, "Width to scale image to (default: 600)") do |width|
|
47
|
+
options[:width] = w
|
48
|
+
end
|
49
|
+
|
50
|
+
# Type of poster (with keyword completion).
|
51
|
+
opts.on("-t", "--type TYPE", [:standard, :classic, :negative],
|
52
|
+
"Poster type (standard, classic, negative), default: classic") do |type|
|
53
|
+
options[:type] = type
|
54
|
+
end
|
55
|
+
|
56
|
+
# Border width
|
57
|
+
opts.on("-r", "--border SIZE", Integer,
|
58
|
+
"Size of border around poster (default: 6)") do |border|
|
59
|
+
options[:lineheight] = border
|
60
|
+
end
|
61
|
+
|
62
|
+
# Background color
|
63
|
+
opts.on("-b", "--background CLR", "Background color (default: black)") do |background|
|
64
|
+
options[:background] = background
|
65
|
+
end
|
66
|
+
|
67
|
+
# Font
|
68
|
+
opts.on("-f", "--font FONT",
|
69
|
+
"Registered with ImageMagick font, or full path to font file (default: Ubuntu-B.ttf)") do |font|
|
70
|
+
options[:font] = font
|
71
|
+
end
|
72
|
+
|
73
|
+
# No argument, shows at tail. This will print an options summary.
|
74
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
75
|
+
puts opts
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
|
79
|
+
end.parse!
|
80
|
+
|
81
|
+
raise "Run `#{$0} --help` for execution examples. Exiting…" if ARGV.size < 3
|
82
|
+
|
83
|
+
if options[:font]
|
84
|
+
unless File.exist?(options[:font])
|
85
|
+
options[:font] = Dir.glob(FONT_PATH).each { |f|
|
86
|
+
break f if File.basename(f) =~ /#{options[:font]}/
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
unless options[:font]
|
91
|
+
ttfs = Dir.glob(FONT_PATH)
|
92
|
+
options[:font] = %w(Ubuntu-B.ttf FreeSans.ttf).each { |ff|
|
93
|
+
result = ttfs.select { |f| File.basename(f) =~ /#{ff}/ }
|
94
|
+
break result.first unless result.empty?
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
Magick::Screwdrivers.info options[:logger], "Font #{options[:font] || DEFAULT} was chosen"
|
99
|
+
|
100
|
+
file = ARGV.shift
|
101
|
+
text1 = ARGV.shift
|
102
|
+
text2 = ARGV.shift
|
103
|
+
dir = ARGV.size.zero? ? File.dirname(file) : ARGV.last
|
104
|
+
`mkdir -p #{dir}`
|
105
|
+
|
106
|
+
outfile = File.basename(file).sub(/(\.\w+)$/, "-poster\\1")
|
107
|
+
Magick::Screwdrivers::poster(file, text1, text2, options).write(File.join dir, outfile) {
|
108
|
+
self.quality = options[:quality] || 90
|
109
|
+
}
|
data/bin/magick_scale
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
6
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
7
|
+
|
8
|
+
require 'rmagick/screwdrivers'
|
9
|
+
|
10
|
+
# options = {
|
11
|
+
# :widths => 600,
|
12
|
+
# :date_in_watermark => false,
|
13
|
+
# :watermark => nil,
|
14
|
+
# :logger => nil
|
15
|
+
# }.merge(options)
|
16
|
+
|
17
|
+
options = {}
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage: #{$0} [options] FILE [DIR]"
|
20
|
+
|
21
|
+
# Verbose?
|
22
|
+
opts.on("-v", "--verbose", "Run verbosely (default: false)") do |v|
|
23
|
+
if v
|
24
|
+
require 'logger'
|
25
|
+
options[:logger] = Logger.new STDOUT
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# Quality
|
29
|
+
opts.on("-q", "--quality [1-100]", Integer, "Quality of resulting JPEG (default: 90)") do |quality|
|
30
|
+
options[:quality] = quality
|
31
|
+
end
|
32
|
+
# Widths
|
33
|
+
opts.on("-w", "--widths X,Y,Z", Array, "Widths of scaled images (default: 600)") do |widths|
|
34
|
+
options[:widths] = widths.map(&:to_i) - [0]
|
35
|
+
end
|
36
|
+
# Puts date on watermark?
|
37
|
+
opts.on("-d", "--date_in_watermark", "Append date to watermark (default: false)") do |dow|
|
38
|
+
options[:date_in_watermark] = dow
|
39
|
+
end
|
40
|
+
# Watermark text
|
41
|
+
opts.on("-m", "--watermark TEXT", "Text to use as watermark (default: none)") do |w|
|
42
|
+
options[:watermark] = w
|
43
|
+
end
|
44
|
+
|
45
|
+
# No argument, shows at tail. This will print an options summary.
|
46
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
47
|
+
puts opts
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
|
51
|
+
end.parse!
|
52
|
+
|
53
|
+
raise "Run `#{$0} --help` for execution examples. Exiting…" if ARGV.size.zero? || ARGV.size > 2
|
54
|
+
|
55
|
+
file = ARGV.first
|
56
|
+
dir = ARGV.size == 2 ? ARGV.last : File.dirname(file)
|
57
|
+
`mkdir -p #{dir}`
|
58
|
+
|
59
|
+
Magick::Screwdrivers::scale(file, options).each { |img|
|
60
|
+
outfile = File.basename(file).sub(/(\.\w+)$/, "-#{img.rows}×#{img.columns}\\1")
|
61
|
+
img.write(File.join dir, outfile) {
|
62
|
+
self.quality = options[:quality] || 90
|
63
|
+
}
|
64
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
Feature: Handy utilities for processing images
|
2
|
+
In order to make RMagick kinda command-line utility
|
3
|
+
We want to provide some handy utility methods
|
4
|
+
|
5
|
+
Scenario: folder of images may be processed to make a collage
|
6
|
+
Given a folder "data/images" is given as the input
|
7
|
+
And an output folder "data/results" is created
|
8
|
+
When I call the collage method
|
9
|
+
And save file to "data/results/collage.jpg"
|
10
|
+
Then the result is created as "data/results/collage.jpg"
|
11
|
+
|
12
|
+
Scenario: image scaling with optional watermark
|
13
|
+
Given an image "data/images/DSCF1354.JPG" is given as the input
|
14
|
+
And an output folder "data/results" is created
|
15
|
+
And everything is logged
|
16
|
+
When I call the scale method with widths 800,600,150
|
17
|
+
And save files with origin "data/results/scaled"
|
18
|
+
Then the result is created as an array of size 3
|
19
|
+
|
20
|
+
Scenario: create standard demotivator from image
|
21
|
+
Given an image "data/images/DSCF1354.JPG" is given as the input
|
22
|
+
And "standard" is given as poster type option
|
23
|
+
And an output folder "data/results" is created
|
24
|
+
And everything is logged
|
25
|
+
When I call the poster method with texts "Hello, there" and "I’m a demotivator"
|
26
|
+
And save file to "data/results/demotivator-standard.jpg"
|
27
|
+
Then the result is created as "data/results/demotivator-standard.jpg"
|
28
|
+
|
29
|
+
Scenario: create classic demotivator from image
|
30
|
+
Given an image "data/images/DSCF1354.JPG" is given as the input
|
31
|
+
And "classic" is given as poster type option
|
32
|
+
And an output folder "data/results" is created
|
33
|
+
And everything is logged
|
34
|
+
When I call the poster method with texts "Hello, there" and "I’m a demotivator"
|
35
|
+
And save file to "data/results/demotivator-classic.jpg"
|
36
|
+
Then the result is created as "data/results/demotivator-classic.jpg"
|
37
|
+
|
38
|
+
Scenario: create negative demotivator from image
|
39
|
+
Given an image "data/images/DSCF1354.JPG" is given as the input
|
40
|
+
And "negative" is given as poster type option
|
41
|
+
And an output folder "data/results" is created
|
42
|
+
And everything is logged
|
43
|
+
When I call the poster method with texts "Hello, there" and "I’m a demotivator"
|
44
|
+
And save file to "data/results/demotivator-negative.jpg"
|
45
|
+
Then the result is created as "data/results/demotivator-negative.jpg"
|
46
|
+
|
47
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
Given(/^a folder "(.*?)" is given as the input$/) do |dir|
|
2
|
+
@input = dir
|
3
|
+
end
|
4
|
+
|
5
|
+
Given(/^an image "(.*?)" is given as the input$/) do |file|
|
6
|
+
@input = file
|
7
|
+
@options = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
Given(/^an output folder "(.*?)" is created$/) do |dir|
|
11
|
+
Dir.mkdir dir rescue "Directory exists, continue…"
|
12
|
+
end
|
13
|
+
|
14
|
+
Given(/^everything is logged$/) do
|
15
|
+
require 'logger'
|
16
|
+
@logger = Logger.new STDOUT
|
17
|
+
end
|
18
|
+
|
19
|
+
Given(/^"(.*?)" is given as poster type option$/) do |type|
|
20
|
+
@options[:type] = type.to_sym
|
21
|
+
@options[:stroke] = '#000000' if @options[:type] == :standard
|
22
|
+
end
|
23
|
+
|
24
|
+
# ===================================================================================
|
25
|
+
|
26
|
+
When(/^I call the collage method$/) do
|
27
|
+
@output = Magick::Screwdrivers::collage @input, {:logger => @logger}
|
28
|
+
end
|
29
|
+
|
30
|
+
When(/^I call the scale method with widths (\d+),(\d+),(\d+)$/) do |arg1, arg2, arg3|
|
31
|
+
opts = {
|
32
|
+
:logger => @logger,
|
33
|
+
:widths => [arg1.to_i,arg2.to_i,arg3.to_i],
|
34
|
+
:date_in_watermark => true,
|
35
|
+
:watermark => 'rmagick-screwdrivers'
|
36
|
+
}
|
37
|
+
@output = Magick::Screwdrivers::scale @input, opts
|
38
|
+
end
|
39
|
+
|
40
|
+
When(/^save file to "(.*?)"$/) do |file|
|
41
|
+
@output.write File.join(file)
|
42
|
+
end
|
43
|
+
|
44
|
+
When(/^save files with origin "(.*?)"$/) do |orig|
|
45
|
+
@output.each { |img|
|
46
|
+
img.write("#{orig}-#{img.rows}.#{img.format.downcase}") { self.quality = 90 }
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
When(/^I call the poster method with texts "(.*?)" and "(.*?)"$/) do |text1, text2|
|
51
|
+
@options[:logger] = @logger
|
52
|
+
@output = Magick::Screwdrivers::poster @input, text1, text2, @options
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# ===================================================================================
|
57
|
+
|
58
|
+
Then(/^the result is created as "(.*?)"$/) do |img|
|
59
|
+
expect(@output.inspect).to match(/#{img}/)
|
60
|
+
end
|
61
|
+
|
62
|
+
Then(/^the result is created as an array of size (\d+)$/) do |arg1|
|
63
|
+
expect(@output.size).to eql(3)
|
64
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "rmagick/screwdrivers/version"
|
2
|
+
require "rmagick/screwdrivers/helpers"
|
3
|
+
require "rmagick/screwdrivers/poster"
|
4
|
+
require "rmagick/screwdrivers/collage"
|
5
|
+
require "rmagick/screwdrivers/scale"
|
6
|
+
|
7
|
+
module Magick
|
8
|
+
module Screwdrivers
|
9
|
+
# Your code goes here...
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'RMagick'
|
4
|
+
|
5
|
+
module Magick
|
6
|
+
module Screwdrivers
|
7
|
+
def self.collage files, options={}
|
8
|
+
options = {
|
9
|
+
:columns => 5,
|
10
|
+
:scale_range => 0.1,
|
11
|
+
:thumb_width => 120,
|
12
|
+
:rotate_angle => 20,
|
13
|
+
:background => 'white',
|
14
|
+
:border => '#DDDDDD',
|
15
|
+
:logger => nil
|
16
|
+
}.merge(options)
|
17
|
+
files = "#{files}/*" if File.directory?(files)
|
18
|
+
imgs = ImageList.new
|
19
|
+
imgnull = Image.new(options[:thumb_width],options[:thumb_width]) {
|
20
|
+
self.background_color = 'transparent'
|
21
|
+
}
|
22
|
+
(options[:columns]+2).times { imgs << imgnull.dup }
|
23
|
+
Dir.glob("#{files}") { |f|
|
24
|
+
begin
|
25
|
+
i = img_from_file(f)
|
26
|
+
rescue
|
27
|
+
warn(options[:logger], "Skipping invalid file #{f}…")
|
28
|
+
next
|
29
|
+
end
|
30
|
+
scale = (1.0 + options[:scale_range]*Random::rand(-1.0..1.0))*options[:thumb_width]/[i.columns, i.rows].max
|
31
|
+
imgs << imgnull.dup if (imgs.size % (options[:columns]+2)).zero?
|
32
|
+
imgs << i.auto_orient.thumbnail(scale).polaroid(
|
33
|
+
Random::rand(-options[:rotate_angle]..options[:rotate_angle])
|
34
|
+
)
|
35
|
+
imgs << imgnull.dup if (imgs.size % (options[:columns]+2)) == options[:columns]+1
|
36
|
+
}
|
37
|
+
(2*options[:columns]+4-(imgs.size % (options[:columns]+2))).times { imgs << imgnull.dup }
|
38
|
+
info options[:logger], "Montaging image [#{options[:columns]}×#{imgs.size/(options[:columns]+2)-2}]"
|
39
|
+
imgs.montage {
|
40
|
+
self.tile = Magick::Geometry.new(options[:columns]+2)
|
41
|
+
self.geometry = "-#{options[:thumb_width]/5}-#{options[:thumb_width]/4}"
|
42
|
+
self.background_color = options[:background]
|
43
|
+
}.trim(true).border(10,10,options[:background]).border(1,1,options[:border])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'RMagick'
|
4
|
+
|
5
|
+
module Magick
|
6
|
+
module Screwdrivers
|
7
|
+
|
8
|
+
# ==============================================================
|
9
|
+
# == Image preparation =================================
|
10
|
+
# ==============================================================
|
11
|
+
|
12
|
+
def self.img_from_file file
|
13
|
+
img = Magick::Image::read(file).first
|
14
|
+
|
15
|
+
# case img.orientation
|
16
|
+
# when Magick::RightTopOrientation
|
17
|
+
# img.rotate!(90)
|
18
|
+
# when Magick::BottomRightOrientation
|
19
|
+
# img.rotate!(180)
|
20
|
+
# when Magick::LeftBottomOrientation
|
21
|
+
# img.rotate!(-90)
|
22
|
+
# end
|
23
|
+
|
24
|
+
img
|
25
|
+
end
|
26
|
+
|
27
|
+
# ==============================================================
|
28
|
+
# == Handy logging =====================================
|
29
|
+
# ==============================================================
|
30
|
+
|
31
|
+
def self.warn logger = nil, msg = nil
|
32
|
+
logger.warn(msg) if logger && logger.respond_to?(:warn)
|
33
|
+
end
|
34
|
+
def self.info logger = nil, msg = nil
|
35
|
+
logger.info(msg) if logger && logger.respond_to?(:info)
|
36
|
+
end
|
37
|
+
def self.debug logger = nil, msg = nil
|
38
|
+
logger.debug(msg) if logger && logger.respond_to?(:debug)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'RMagick'
|
4
|
+
|
5
|
+
module Magick
|
6
|
+
module Screwdrivers
|
7
|
+
def self.poster file, text1, text2, options={}
|
8
|
+
options = {
|
9
|
+
:color => '#FFFFFF',
|
10
|
+
:stroke => nil,
|
11
|
+
:width => 600,
|
12
|
+
:type => :classic, # :classic for black square around
|
13
|
+
:lineheight => 6,
|
14
|
+
:background => '#000000',
|
15
|
+
:font => '/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf',
|
16
|
+
:max_font_size => 48,
|
17
|
+
:logger => nil
|
18
|
+
}.merge(options)
|
19
|
+
|
20
|
+
text1 ||= ''
|
21
|
+
text2 ||= ''
|
22
|
+
options[:lineheight] = 3 if options[:lineheight] < 3
|
23
|
+
|
24
|
+
img = img_from_file(file)
|
25
|
+
img.thumbnail!(options[:width].to_f/img.columns.to_f)
|
26
|
+
|
27
|
+
mark = Magick::Image.new(img.columns, img.rows) do
|
28
|
+
self.background_color = options[:type] == :classic ? options[:background] : 'transparent'
|
29
|
+
end
|
30
|
+
|
31
|
+
gc = Magick::Draw.new
|
32
|
+
|
33
|
+
pointsize = [img.columns, options[:max_font_size]].min
|
34
|
+
classic_margin = 0
|
35
|
+
|
36
|
+
loop do
|
37
|
+
gc.pointsize = pointsize -= 1 + pointsize / 33
|
38
|
+
|
39
|
+
m1 = gc.get_type_metrics(text1)
|
40
|
+
w1 = m1.width
|
41
|
+
h1 = (m1.bounds.y2 - m1.bounds.y1).round
|
42
|
+
|
43
|
+
m2 = gc.get_type_metrics(text2)
|
44
|
+
w2 = m2.width
|
45
|
+
h2 = (m2.bounds.y2 - m2.bounds.y1).round
|
46
|
+
|
47
|
+
if w1 < img.columns - 10*options[:lineheight] && w2 < img.columns - 10*options[:lineheight]
|
48
|
+
if options[:type] == :classic
|
49
|
+
classic_margin = h2
|
50
|
+
mark.resize! img.columns+options[:lineheight]*14/3, img.rows+options[:lineheight]*8+h1+h2
|
51
|
+
gc.stroke_width = options[:lineheight]/3
|
52
|
+
gc.stroke = '#FFFFFF'
|
53
|
+
gc.fill = options[:background]
|
54
|
+
gc.rectangle(options[:lineheight]*7/6, options[:lineheight]*7/6, \
|
55
|
+
img.columns+options[:lineheight]*21/6, img.rows+options[:lineheight]*21/6)
|
56
|
+
gc.composite(
|
57
|
+
7*options[:lineheight]/3, 7*options[:lineheight]/3,
|
58
|
+
0, 0,
|
59
|
+
img, Magick::OverCompositeOp
|
60
|
+
)
|
61
|
+
gc.draw mark
|
62
|
+
end
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
gc.fill = options[:color]
|
68
|
+
gc.stroke = options[:stroke] || 'none'
|
69
|
+
gc.stroke_width = 1
|
70
|
+
gc.font = options[:font]
|
71
|
+
|
72
|
+
case options[:type]
|
73
|
+
when :classic
|
74
|
+
gc.annotate(mark, 0, 0, 0, classic_margin+2*options[:lineheight], text1) do
|
75
|
+
self.gravity = Magick::SouthGravity
|
76
|
+
end
|
77
|
+
else
|
78
|
+
gc.annotate(mark, 0, 0, 0, 0, text1) do
|
79
|
+
self.gravity = Magick::NorthGravity
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
gc.annotate(mark, 0, 0, 0, 0, text2) do
|
84
|
+
self.gravity = Magick::SouthGravity
|
85
|
+
end
|
86
|
+
|
87
|
+
case options[:type]
|
88
|
+
when :classic
|
89
|
+
mark
|
90
|
+
when :negative
|
91
|
+
img.composite(mark, Magick::SouthEastGravity, Magick::SubtractCompositeOp)
|
92
|
+
when :standard
|
93
|
+
img.composite(mark, Magick::SouthEastGravity, Magick::OverCompositeOp)
|
94
|
+
else
|
95
|
+
warn options[:logger], "Invalid type: #{options[:type]}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'RMagick'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
module Magick
|
7
|
+
module Screwdrivers
|
8
|
+
def self.scale file, options={}
|
9
|
+
options = {
|
10
|
+
:widths => 600,
|
11
|
+
:date_in_watermark => false,
|
12
|
+
:watermark => nil,
|
13
|
+
:logger => nil
|
14
|
+
}.merge(options)
|
15
|
+
|
16
|
+
img = img_from_file file
|
17
|
+
|
18
|
+
date = img.get_exif_by_number(36867)[36867]
|
19
|
+
date = Date.parse(date.gsub(/:/, '/')) if date
|
20
|
+
date ||= Date.parse(img.properties['exif:DateTime'].gsub(/:/, '/')) if img.properties['exif:DateTime']
|
21
|
+
date ||= Date.parse(img.properties['date:modify']) if img.properties['date:modify']
|
22
|
+
date ||= Date.parse(img.properties['date:create']) if img.properties['date:create']
|
23
|
+
date ||= Date.parse(img.properties['xap:CreateDate']) if img.properties['xap:CreateDate']
|
24
|
+
|
25
|
+
options[:watermark] = ([
|
26
|
+
options[:watermark], date.strftime('%y/%m/%d')] - [nil]
|
27
|
+
).join(' :: ').strip if options[:date_in_watermark]
|
28
|
+
|
29
|
+
result = ImageList.new
|
30
|
+
|
31
|
+
[*options[:widths]].each { |sz|
|
32
|
+
unless Integer === sz && sz < img.columns && sz > 0
|
33
|
+
warn options[:logger], "Invalid width #{sz} (original is #{img.columns}), skipping…"
|
34
|
+
next
|
35
|
+
end
|
36
|
+
|
37
|
+
curr = img.resize_to_fit(sz)
|
38
|
+
|
39
|
+
if will_wm = (options[:watermark] && curr.rows >= 400)
|
40
|
+
mark = Magick::Image.new(curr.rows, curr.columns) do
|
41
|
+
self.background_color = 'transparent'
|
42
|
+
end
|
43
|
+
draw = Magick::Draw.new
|
44
|
+
draw.encoding = 'Unicode'
|
45
|
+
draw.annotate(mark, 0, 0, 5, 2, options[:watermark]) do
|
46
|
+
self.encoding = 'Unicode'
|
47
|
+
self.gravity = Magick::SouthEastGravity
|
48
|
+
self.fill 'rgba(60%, 60%, 60%, 0.40)'
|
49
|
+
self.stroke = 'none'
|
50
|
+
self.pointsize = 1 + 2 * Math.log(sz, 3).to_i
|
51
|
+
self.font_family = 'Comfortaa'
|
52
|
+
self.font_weight = Magick::NormalWeight
|
53
|
+
self.font_style = Magick::NormalStyle
|
54
|
+
end
|
55
|
+
curr = curr.composite(mark.rotate(-90), Magick::SouthEastGravity, Magick::SubtractCompositeOp)
|
56
|
+
end
|
57
|
+
|
58
|
+
info options[:logger], "Scaling to width #{curr.rows}×#{curr.columns}, watermark: “#{will_wm ? options[:watermark] : 'NONE'}”"
|
59
|
+
|
60
|
+
result << curr
|
61
|
+
}
|
62
|
+
|
63
|
+
result
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require 'rmagick/screwdrivers/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'rmagick-screwdrivers'
|
6
|
+
s.version = Magick::Screwdrivers::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.date = '2013-10-26'
|
9
|
+
s.authors = ['Alexei Matyushkin']
|
10
|
+
s.email = 'am@mudasobwa.ru'
|
11
|
+
s.homepage = 'http://github.com/mudasobwa/rmagick-screwdrivers'
|
12
|
+
s.summary = %Q{RMagick addons for utilizing some common tasks}
|
13
|
+
s.description = %Q{Creating collages, demotivators and other handy stuff with RMagick}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
'LICENSE',
|
16
|
+
'README.md',
|
17
|
+
]
|
18
|
+
|
19
|
+
s.required_rubygems_version = Gem::Requirement.new('>= 1.3.7')
|
20
|
+
s.rubygems_version = '1.3.7'
|
21
|
+
s.specification_version = 3
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
s.require_paths = ['lib']
|
27
|
+
|
28
|
+
s.add_development_dependency 'rspec'
|
29
|
+
s.add_development_dependency 'yard'
|
30
|
+
s.add_development_dependency 'cucumber'
|
31
|
+
s.add_development_dependency 'yard-cucumber'
|
32
|
+
s.add_development_dependency 'bueller'
|
33
|
+
|
34
|
+
s.add_dependency 'rmagick'
|
35
|
+
end
|
36
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
require 'rmagick-screwdrivers'
|
4
|
+
|
5
|
+
# Requires supporting files with custom matchers and macros, etc,
|
6
|
+
# in ./support/ and its subdirectories.
|
7
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rmagick-screwdrivers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexei Matyushkin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: yard
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cucumber
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard-cucumber
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bueller
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rmagick
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Creating collages, demotivators and other handy stuff with RMagick
|
98
|
+
email: am@mudasobwa.ru
|
99
|
+
executables:
|
100
|
+
- magick_collage
|
101
|
+
- magick_poster
|
102
|
+
- magick_scale
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files:
|
105
|
+
- LICENSE
|
106
|
+
- README.md
|
107
|
+
files:
|
108
|
+
- .document
|
109
|
+
- .gitignore
|
110
|
+
- Gemfile
|
111
|
+
- LICENSE
|
112
|
+
- README.md
|
113
|
+
- Rakefile
|
114
|
+
- bin/magick_collage
|
115
|
+
- bin/magick_poster
|
116
|
+
- bin/magick_scale
|
117
|
+
- features/rmagick-screwdrivers.feature
|
118
|
+
- features/step_definitions/rmagick-screwdrivers_steps.rb
|
119
|
+
- features/support/env.rb
|
120
|
+
- lib/rmagick/screwdrivers.rb
|
121
|
+
- lib/rmagick/screwdrivers/collage.rb
|
122
|
+
- lib/rmagick/screwdrivers/helpers.rb
|
123
|
+
- lib/rmagick/screwdrivers/poster.rb
|
124
|
+
- lib/rmagick/screwdrivers/scale.rb
|
125
|
+
- lib/rmagick/screwdrivers/version.rb
|
126
|
+
- rmagick-screwdrivers.gemspec
|
127
|
+
- spec/rmagick-screwdrivers_spec.rb
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
homepage: http://github.com/mudasobwa/rmagick-screwdrivers
|
130
|
+
licenses: []
|
131
|
+
metadata: {}
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ! '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 1.3.7
|
146
|
+
requirements: []
|
147
|
+
rubyforge_project:
|
148
|
+
rubygems_version: 2.0.2
|
149
|
+
signing_key:
|
150
|
+
specification_version: 3
|
151
|
+
summary: RMagick addons for utilizing some common tasks
|
152
|
+
test_files: []
|
153
|
+
has_rdoc:
|