rmagick-screwdrivers 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|