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.
@@ -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
@@ -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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
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.
@@ -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
+
@@ -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
@@ -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
+ }
@@ -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
+ }
@@ -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,5 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'rmagick/screwdrivers'
4
+
5
+ require 'rspec/expectations'
@@ -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,5 @@
1
+ module Magick
2
+ module Screwdrivers
3
+ VERSION = "0.1.1"
4
+ end
5
+ 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
+
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe RmagickScrewdrivers do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -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: