mapel 0.1.5
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/.gitignore +3 -0
- data/README.rdoc +21 -0
- data/Rakefile +54 -0
- data/lib/mapel.rb +188 -0
- data/mapel.gemspec +50 -0
- data/spec/fixtures/ImageMagick.jpg +0 -0
- data/spec/mapel_spec.rb +63 -0
- data/spec/spec_helper.rb +6 -0
- metadata +72 -0
data/.gitignore
ADDED
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,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
name = 'mapel'
|
5
|
+
version = '0.1.5'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = name
|
11
|
+
gem.version = version
|
12
|
+
gem.summary = %Q{A dead-simple image rendering DSL.}
|
13
|
+
gem.email = "alekswilliams@earthlink.net"
|
14
|
+
gem.homepage = "http://github.com/akdubya/mapel"
|
15
|
+
gem.authors = ["Aleksander Williams"]
|
16
|
+
gem.add_development_dependency "bacon", ">= 0"
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
Rake::TestTask.new(:spec) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
28
|
+
spec.verbose = true
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'rcov/rcovtask'
|
33
|
+
Rcov::RcovTask.new do |spec|
|
34
|
+
spec.libs << 'spec'
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.verbose = true
|
37
|
+
end
|
38
|
+
rescue LoadError
|
39
|
+
task :rcov do
|
40
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
task :spec => :check_dependencies
|
45
|
+
|
46
|
+
task :default => :spec
|
47
|
+
|
48
|
+
require 'rake/rdoctask'
|
49
|
+
Rake::RDocTask.new do |rdoc|
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "rack-thumb #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
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.respond_to?(:key) ? RFLAGS.key(@flag) : RFLAGS.index(@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
|
data/mapel.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{mapel}
|
8
|
+
s.version = "0.1.5"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Aleksander Williams"]
|
12
|
+
s.date = %q{2010-02-24}
|
13
|
+
s.email = %q{alekswilliams@earthlink.net}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README.rdoc"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
".gitignore",
|
19
|
+
"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"lib/mapel.rb",
|
22
|
+
"mapel.gemspec",
|
23
|
+
"spec/fixtures/ImageMagick.jpg",
|
24
|
+
"spec/mapel_spec.rb",
|
25
|
+
"spec/spec_helper.rb"
|
26
|
+
]
|
27
|
+
s.homepage = %q{http://github.com/akdubya/mapel}
|
28
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
s.rubygems_version = %q{1.3.5}
|
31
|
+
s.summary = %q{A dead-simple image rendering DSL.}
|
32
|
+
s.test_files = [
|
33
|
+
"spec/spec_helper.rb",
|
34
|
+
"spec/mapel_spec.rb"
|
35
|
+
]
|
36
|
+
|
37
|
+
if s.respond_to? :specification_version then
|
38
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
39
|
+
s.specification_version = 3
|
40
|
+
|
41
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
42
|
+
s.add_development_dependency(%q<bacon>, [">= 0"])
|
43
|
+
else
|
44
|
+
s.add_dependency(%q<bacon>, [">= 0"])
|
45
|
+
end
|
46
|
+
else
|
47
|
+
s.add_dependency(%q<bacon>, [">= 0"])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
Binary file
|
data/spec/mapel_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mapel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aleksander Williams
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-24 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bacon
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: alekswilliams@earthlink.net
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.rdoc
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- lib/mapel.rb
|
38
|
+
- mapel.gemspec
|
39
|
+
- spec/fixtures/ImageMagick.jpg
|
40
|
+
- spec/mapel_spec.rb
|
41
|
+
- spec/spec_helper.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/akdubya/mapel
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: A dead-simple image rendering DSL.
|
70
|
+
test_files:
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
- spec/mapel_spec.rb
|