akdubya-mapel 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc ADDED
@@ -0,0 +1,21 @@
1
+ == Mapel: A dead-simple image-rendering DSL
2
+
3
+ <tt>Mapel</tt> is a dead-simple, chainable image-rendering DSL for ImageMagick.
4
+ Still very much an experiment-in-progress, it supports a dozen or so essential
5
+ commands.
6
+
7
+ Example:
8
+
9
+ Mapel.render('image.jpg').resize("50%").to('output.jpg').run
10
+
11
+ == Fun Fact
12
+
13
+ <tt>Mapel</tt> is named after a tortoiseshell cat.
14
+
15
+ == Meta
16
+
17
+ Written by Aleks Williams (http://github.com/akdubya)
18
+
19
+ Released under the MIT License: www.opensource.org/licenses/mit-license.php
20
+
21
+ github.com/akdubya/mapel
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require 'fileutils'
7
+
8
+ task :default => [:test]
9
+ task :spec => :test
10
+
11
+ name = 'mapel'
12
+ version = '0.1.3'
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = name
16
+ s.version = version
17
+ s.summary = "A dead-simple image-rendering DSL."
18
+ s.description = "A dead-simple image-rendering DSL."
19
+ s.author = "Aleksander Williams"
20
+ s.email = "alekswilliams@earthlink.net"
21
+ s.homepage = "http://github.com/akdubya/mapel"
22
+ s.platform = Gem::Platform::RUBY
23
+ s.has_rdoc = true
24
+ s.files = %w(Rakefile README.rdoc) + Dir.glob("{lib,spec}/**/*")
25
+ s.require_path = "lib"
26
+ end
27
+
28
+ Rake::GemPackageTask.new(spec) do |p|
29
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
30
+ end
31
+
32
+ desc "Install as a system gem"
33
+ task :install => [ :package ] do
34
+ sh %{sudo gem install pkg/#{name}-#{version}.gem}
35
+ end
36
+
37
+ desc "Uninstall as a system gem"
38
+ task :uninstall => [ :clean ] do
39
+ sh %{sudo gem uninstall #{name}}
40
+ end
41
+
42
+ desc "Create a gemspec file"
43
+ task :make_spec do
44
+ File.open("#{name}.gemspec", "w") do |file|
45
+ file.puts spec.to_ruby
46
+ end
47
+ end
48
+
49
+ Rake::TestTask.new(:test) do |t|
50
+ t.libs << "spec"
51
+ t.test_files = FileList['spec/*_spec.rb']
52
+ t.verbose = true
53
+ end
54
+
55
+ Rake::RDocTask.new do |t|
56
+ t.rdoc_dir = 'rdoc'
57
+ t.title = "Mapel: A dead-simple image-rendering DSL."
58
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
59
+ t.options << '--charset' << 'utf-8'
60
+ t.rdoc_files.include('README.rdoc')
61
+ t.rdoc_files.include('lib/mapel.rb')
62
+ end
data/lib/mapel.rb ADDED
@@ -0,0 +1,188 @@
1
+ def Mapel(source)
2
+ Mapel.render(source)
3
+ end
4
+
5
+ module Mapel
6
+
7
+ # Mapel.info('image.jpg')
8
+ def self.info(source, engine = :image_magick)
9
+ Mapel::Engine.const_get(camelize(engine)).info(source)
10
+ end
11
+
12
+ # Mapel.render('image.jpg').resize("50%").to('output.jpg').run
13
+ def self.render(source, engine = :image_magick)
14
+ Mapel::Engine.const_get(camelize(engine)).render(source)
15
+ end
16
+
17
+ # Mapel.list
18
+ def self.list(engine = :image_magick)
19
+ Mapel::Engine.const_get(camelize(engine)).list
20
+ end
21
+
22
+ class Engine
23
+ attr_reader :command, :status, :output
24
+ attr_accessor :commands
25
+
26
+ def initialize(source = nil)
27
+ @source = source
28
+ @commands = []
29
+ end
30
+
31
+ def success?
32
+ @status
33
+ end
34
+
35
+ class ImageMagick < Engine
36
+ def self.info(source)
37
+ im = new(source)
38
+ im.commands << 'identify'
39
+ im.commands << source
40
+ im.run.to_info_hash
41
+ end
42
+
43
+ def self.render(source = nil)
44
+ im = new(source)
45
+ im.commands << 'convert'
46
+ im.commands << source unless source.nil?
47
+ im
48
+ end
49
+
50
+ def self.list(type = nil)
51
+ im = new
52
+ im.commands << 'convert -list'
53
+ im.run
54
+ end
55
+
56
+ def crop(*args)
57
+ @commands << "-crop \"#{Geometry.new(*args).to_s(true)}\""
58
+ self
59
+ end
60
+
61
+ def gravity(type = :center)
62
+ @commands << "-gravity #{type}"
63
+ self
64
+ end
65
+
66
+ def repage
67
+ @commands << "+repage"
68
+ self
69
+ end
70
+
71
+ def resize(*args)
72
+ @commands << "-resize \"#{Geometry.new(*args)}\""
73
+ self
74
+ end
75
+
76
+ def resize!(*args)
77
+ @commands << lambda {
78
+ cmd = self.class.new
79
+ width, height = Geometry.new(*args).dimensions
80
+ if @source
81
+ origin_width, origin_height = Mapel.info(@source)[:dimensions]
82
+
83
+ # Crop dimensions should not exceed original dimensions.
84
+ width = [width, origin_width].min
85
+ height = [height, origin_height].min
86
+
87
+ if width != origin_width || height != origin_height
88
+ scale = [width/origin_width.to_f, height/origin_height.to_f].max
89
+ cmd.resize((scale*(origin_width+0.5)), (scale*(origin_height+0.5)))
90
+ end
91
+ cmd.crop(width, height).to_preview
92
+ else
93
+ "\{crop_resized #{args}\}"
94
+ end
95
+ }
96
+ self
97
+ end
98
+
99
+ def scale(*args)
100
+ @commands << "-scale \"#{Geometry.new(*args)}\""
101
+ self
102
+ end
103
+
104
+ def to(path)
105
+ @commands << path
106
+ self
107
+ end
108
+
109
+ def undo
110
+ @commands.pop
111
+ self
112
+ end
113
+
114
+ def run
115
+ @output = `#{to_preview}`
116
+ @status = ($? == 0)
117
+ self
118
+ end
119
+
120
+ def to_preview
121
+ @commands.map { |cmd| cmd.respond_to?(:call) ? cmd.call : cmd }.join(' ')
122
+ end
123
+
124
+ def to_info_hash
125
+ meta = {}
126
+ meta[:dimensions] = @output.split(' ')[2].split('x').map { |d| d.to_i }
127
+ meta
128
+ end
129
+ end
130
+ end
131
+
132
+ class Geometry
133
+ attr_accessor :width, :height, :x, :y, :flag
134
+
135
+ FLAGS = ['', '%', '<', '>', '!', '@']
136
+ RFLAGS = {
137
+ '%' => :percent,
138
+ '!' => :aspect,
139
+ '<' => :<,
140
+ '>' => :>,
141
+ '@' => :area
142
+ }
143
+
144
+ # Regex parser for geometry strings
145
+ RE = /\A(\d*)(?:x(\d+)?)?([-+]\d+)?([-+]\d+)?([%!<>@]?)\Z/
146
+
147
+ def initialize(*args)
148
+ if (args.length == 1) && (args.first.kind_of?(String))
149
+ raise(ArgumentError, "Invalid geometry string") unless m = RE.match(args.first)
150
+ args = m.to_a[1..5]
151
+ args[4] = args[4] ? RFLAGS[args[4]] : nil
152
+ end
153
+ @width = args[0] ? args[0].to_i.round : 0
154
+ @height = args[1] ? args[1].to_i.round : 0
155
+ raise(ArgumentError, "Width must be >= 0") if @width < 0
156
+ raise(ArgumentError, "Height must be >= 0") if @height < 0
157
+ @x = args[2] ? args[2].to_i : 0
158
+ @y = args[3] ? args[3].to_i : 0
159
+ @flag = (args[4] && RFLAGS.has_value?(args[4])) ? args[4] : nil
160
+ end
161
+
162
+ def dimensions
163
+ [width, height]
164
+ end
165
+
166
+ # Convert object to a geometry string
167
+ def to_s(crop = false)
168
+ str = ''
169
+ str << "%g" % @width if @width > 0
170
+ str << 'x' if @height > 0
171
+ str << "%g" % @height if @height > 0
172
+ str << "%+d%+d" % [@x, @y] if (@x != 0 || @y != 0 || crop)
173
+ str << RFLAGS.key(@flag).to_s
174
+ end
175
+ end
176
+
177
+ # By default, camelize converts strings to UpperCamelCase.
178
+ #
179
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
180
+ #
181
+ # @example
182
+ # "active_record".camelize #=> "ActiveRecord"
183
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
184
+ #
185
+ def self.camelize(word, *args)
186
+ word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
187
+ end
188
+ end
Binary file
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe Mapel do
4
+ before do
5
+ @input = File.dirname(__FILE__) + '/fixtures'
6
+ @output = File.dirname(__FILE__) + '/output'
7
+ @logo = @input + '/ImageMagick.jpg'
8
+ end
9
+
10
+ after do
11
+ Dir.glob(@output + '/*') { |f| File.delete(f) }
12
+ end
13
+
14
+ it "should respond to #info" do
15
+ Mapel.respond_to?(:info).should == true
16
+ end
17
+
18
+ it "should respond to #render" do
19
+ Mapel.respond_to?(:render).should == true
20
+ end
21
+
22
+ it "should support compact rendering syntax" do
23
+ Mapel(@logo).should.be.kind_of(Mapel::Engine)
24
+ end
25
+
26
+ describe "#info" do
27
+ it "should return image dimensions" do
28
+ Mapel.info(@logo)[:dimensions].should == [572, 591]
29
+ end
30
+ end
31
+
32
+ describe "#render" do
33
+ it "should be able to scale an image" do
34
+ cmd = Mapel(@logo).scale('50%').to(@output + '/scaled.jpg').run
35
+ cmd.status.should == true
36
+ Mapel.info(@output + '/scaled.jpg')[:dimensions].should == [286, 296]
37
+ end
38
+
39
+ it "should be able to crop an image" do
40
+ cmd = Mapel(@logo).crop('50x50+0+0').to(@output + '/cropped.jpg').run
41
+ cmd.status.should == true
42
+ Mapel.info(@output + '/cropped.jpg')[:dimensions].should == [50, 50]
43
+ end
44
+
45
+ it "should be able to resize an image" do
46
+ cmd = Mapel(@logo).resize('100x').to(@output + '/resized.jpg').run
47
+ cmd.status.should == true
48
+ Mapel.info(@output + '/resized.jpg')[:dimensions].should == [100, 103]
49
+ end
50
+
51
+ it "should be able to crop-resize an image" do
52
+ cmd = Mapel(@logo).gravity(:west).resize!('50x100').to(@output + '/crop_resized.jpg').run
53
+ cmd.status.should == true
54
+ Mapel.info(@output + '/crop_resized.jpg')[:dimensions].should == [50, 99]
55
+ end
56
+
57
+ it "should allow arbitrary addition of commands to the queue" do
58
+ cmd = Mapel(@logo).gravity(:west)
59
+ cmd.resize(50, 50)
60
+ cmd.to_preview.should == "convert #{@logo} -gravity west -resize \"50x50\""
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'bacon'
3
+
4
+ require File.dirname(__FILE__) + '/../lib/mapel'
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: akdubya-mapel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Aleksander Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-11 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A dead-simple image-rendering DSL.
17
+ email: alekswilliams@earthlink.net
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - README.rdoc
27
+ - lib/mapel.rb
28
+ - spec/fixtures
29
+ - spec/fixtures/ImageMagick.jpg
30
+ - spec/spec_helper.rb
31
+ - spec/output
32
+ - spec/mapel_spec.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/akdubya/mapel
35
+ post_install_message:
36
+ rdoc_options: []
37
+
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ requirements: []
53
+
54
+ rubyforge_project:
55
+ rubygems_version: 1.2.0
56
+ signing_key:
57
+ specification_version: 2
58
+ summary: A dead-simple image-rendering DSL.
59
+ test_files: []
60
+