magic_door 0.1.0b

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Daniel Mircea, OkapiStudio
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.rdoc ADDED
@@ -0,0 +1,75 @@
1
+ = MagicDoor Generator
2
+
3
+ Generates custom button images with RMagick using the famous {sliding doors css tehnique}[http://www.alistapart.com/articles/slidingdoors].
4
+
5
+
6
+ == A brief introduction
7
+
8
+ While it can be argued that this functionality is being preceded by CSS 3, this code provides a happy crossbrowser experience, backed by the ability of using copyrighted fonts.
9
+
10
+ The script works by splitting a large wide image into two narrow ones that accomodate the length of a provided text. The location where the split ocurrs can be specified as an optional parameter.
11
+
12
+
13
+ == Generating buttons
14
+
15
+ Bellow you'll find a simple example of creating an image button:
16
+
17
+ options = {
18
+ :text => "Please click me"
19
+ :image => "wide-button-template.png"
20
+ :file_name => "click-me.png",
21
+ :css => "color: #333; font-size: 25"
22
+ }
23
+
24
+ file = MagicDoor.new(options)
25
+ file.save
26
+
27
+ You may set defaults in order to make subsequent calls simpler:
28
+
29
+ MagicDoor.defaults.merge!({
30
+ :source_path => "/project/path/buttons",
31
+ :destination_path => "/project/path/buttons/cache",
32
+ :split_at => 11,
33
+ :css => "padding: 10;
34
+ font-weight: bold;
35
+ font-size: 14;
36
+ color: #fff; text-align: center;
37
+ text-shadow: #cecefe 0 1;
38
+ font: '/project/path/fonts/MyriadPro-Semibold.otf"
39
+ })
40
+
41
+
42
+ == Limitations
43
+
44
+ All CSS sizes are calculated in pixels, so you should not add any measurement units after the numbers.
45
+
46
+ The padding property does not affect the top and bottom region.
47
+
48
+ Parsing the CSS and mapping it to RMagick is somewhat tendious, as such there are only a few css properties supported. Please consult the docs for a complete list.
49
+
50
+
51
+ == Command Line
52
+
53
+ You can run MagicDoor from the command line:
54
+
55
+ $ magic-door -t "hello world" -i "/home/daniel/Desktop/button.png" -f "/home/daniel/hello.png"
56
+
57
+
58
+ == Source code
59
+
60
+ The source code is hosted on Github: http://github.com/viseztrance/magic_door
61
+
62
+ To get MagicDoor from source:
63
+
64
+ git clone git@github.com:viseztrance/magic_door.git
65
+
66
+
67
+ == Acknowledgements
68
+
69
+ I would like to thank {OkapiStudio}[http://okapistudio.com] - my employer for allowing me to share this code.
70
+
71
+
72
+ == License
73
+
74
+ This package is licensed under the MIT license and/or the Creative
75
+ Commons Attribution-ShareAlike.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake/rdoctask'
2
+
3
+
4
+ spec = Gem::Specification.load(File.expand_path("magic_door.gemspec", File.dirname(__FILE__)))
5
+
6
+ # Create the documentation.
7
+ Rake::RDocTask.new do |rdoc|
8
+ rdoc.rdoc_files.include "README.rdoc", "lib/**/*.rb"
9
+ rdoc.options = spec.rdoc_options
10
+ end
11
+
12
+ desc "Push new release to rubyforge and git tag"
13
+ task :push do
14
+ sh "git push"
15
+ puts "Tagging version #{spec.version} .."
16
+ sh "git tag v#{spec.version}"
17
+ sh "git push --tag"
18
+ puts "Building and pushing gem .."
19
+ sh "gem build #{spec.name}.gemspec"
20
+ sh "gem push #{spec.name}-#{spec.version}.gem"
21
+ end
22
+
23
+ desc "Install #{spec.name} locally"
24
+ task :install do
25
+ sh "gem build #{spec.name}.gemspec"
26
+ sh "gem install #{spec.name}-#{spec.version}.gem"
27
+ end
data/bin/magic-door ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require "magic_door"
5
+
6
+ options = {}
7
+
8
+ ARGV.options do |opt|
9
+ script_name = File.basename($0)
10
+
11
+ opt.set_summary_indent(" ")
12
+ opt.banner = "Usage: #{script_name} [options]"
13
+ opt.define_head "Generates custom button images with RMagick using the famous sliding doors css tehnique."
14
+ opt.separator ""
15
+
16
+ opt.on("-t", "--text=val", String,
17
+ "Text shown on the button, eg. \"click me\"") { |options[:text]| }
18
+ opt.on("-i", "--image=val", String,
19
+ "Path to the existing image that will be used as a template, eg. \"/home/me/rails/public/images/button.png\"") { |options[:image]| }
20
+ opt.on("-f", "--file=val", String,
21
+ "Path of the saved image, eg. \"/home/me/rails/public/images/cache/click-me.png\"") { |options[:file_name]| }
22
+ opt.on("-c", "--css=val", String,
23
+ "Css expression, eg. \"font-weight: bold; font-size: 25\"") { |options[:css]| }
24
+ opt.on("-s", "--split=val", String,
25
+ "The location where the template image be split in two.") { |options[:split_at]| }
26
+
27
+ opt.separator ""
28
+
29
+ opt.on_tail("-h", "--help", "Show this help message.") { puts opt; exit }
30
+ opt.on_tail("-v", "--version", "Show version") { puts script_name + ' ' + MagicDoor::VERSION; exit }
31
+
32
+ opt.parse!
33
+ end
34
+
35
+ if options[:text].nil? || options[:image].nil? || options[:file_name].nil?
36
+ puts %q|Insufficient arguments.
37
+ The "text" (-t), "image" (-i) and "file" (-f) options are required. -h for additional information.|
38
+ else
39
+ saved_file = MagicDoor.new(options).save
40
+ puts "Wrote #{saved_file.inspect}."
41
+ end
data/lib/magic_door.rb ADDED
@@ -0,0 +1,220 @@
1
+ #
2
+ # = magic_door.rb - MagicDoor generator
3
+ #
4
+ # Author:: Daniel Mircea daniel@viseztrance.com
5
+ # Copyright:: Copyright (c) 2010 Daniel Mircea, OkapiStudio
6
+ # License:: MIT and/or Creative Commons Attribution-ShareAlike
7
+
8
+ require "RMagick"
9
+ require "fileutils"
10
+
11
+ class MagicDoor
12
+
13
+ VERSION = Gem::Specification.load(File.expand_path("../magic_door.gemspec", File.dirname(__FILE__))).version.to_s
14
+
15
+ module CssMethods #:nodoc:
16
+
17
+ def set_properties(css)
18
+
19
+ css.each do |args|
20
+ attribute = MagicDoor::CSS_ALIASES[:attributes][args.first] || args.first
21
+ value = proc {
22
+ v = MagicDoor::CSS_ALIASES[:values][args.last] || args.last
23
+ Float(v) rescue v
24
+ }.call
25
+ self.send("#{attribute}=", value)
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ CSS_ALIASES = {
33
+
34
+ :attributes => {
35
+ "color" => "fill",
36
+ "font-weight" => "font_weight",
37
+ "font-family" => "font_family",
38
+ "font-size" => "pointsize",
39
+ "text-align" => "gravity",
40
+ },
41
+
42
+ :values => {
43
+ "bold" => Magick::BoldWeight,
44
+ "center" => Magick::CenterGravity,
45
+ "left" => Magick::WestGravity,
46
+ "right" => Magick::EastGravity
47
+ }
48
+
49
+ }
50
+
51
+ attr_reader :css,
52
+ :image
53
+
54
+
55
+ attr_accessor :text,
56
+ :split_at,
57
+ :source_path,
58
+ :destination_path,
59
+ :file_name
60
+
61
+ attr_accessor :canvas #:nodoc:
62
+
63
+ @@defaults = {
64
+ :css => "text-align: center; padding: 10; color: #000",
65
+ :split_at => 30,
66
+ :image => "button.png",
67
+ :source_path => "",
68
+ :destination_path => "",
69
+ :text => "Submit",
70
+ :file_name => "result.png"
71
+ }
72
+
73
+ class << self
74
+
75
+ # Sets +defaults+ for new object instances.
76
+ def defaults=(options)
77
+ @@defaults = options
78
+ end
79
+
80
+ # Returns defined defaults
81
+ def defaults
82
+ @@defaults
83
+ end
84
+
85
+ end
86
+
87
+ # Instantiate a new object.
88
+ # Missing options fallback to +defaults+.
89
+ # ==== Options:
90
+ # [*text*] Text that will be displayed inside the button.
91
+ # [*split_at*] Location where the image is being split.
92
+ # [*source_path*] Prefix for +image+ location.
93
+ # [*destination_path*] Prefix for +file_name+ location.
94
+ # [*file_name*] The name of the file being saved.
95
+ def initialize(options = {})
96
+ self.css = self.class.defaults[:css]
97
+ self.css = options[:css] if options[:css]
98
+ %w(source_path destination_path split_at image text file_name).each do |name|
99
+ n = name.to_sym
100
+ self.send("#{n}=", options[n] || self.class.defaults[n])
101
+ end
102
+ end
103
+
104
+ # Creates the image file.
105
+ # ==== Returns
106
+ # Path of the saved file.
107
+ # ==== Options
108
+ # [*as*] Alternate way of setting the name of the file being saved. Fallsback to +file_name+.
109
+ def save(options = {})
110
+ compose_button
111
+ draw
112
+ write_file(options[:as] || file_name)
113
+ end
114
+
115
+ # Sets the template image.
116
+ #
117
+ # Will fail if the image does not exist.
118
+ def image=(name)
119
+ @image = Magick::Image.read(File.join(source_path, name)).first
120
+ end
121
+
122
+ # Evaluates CSS expressions.
123
+ #
124
+ # Gracefuly merges the CSS defined in +defaults+.
125
+ #
126
+ # Sizes are being calculated in pixels. Don't specify any measuring units at all.
127
+ # ==== Available expressions:
128
+ # * color
129
+ # * font-weight
130
+ # * font-family
131
+ # * font-size
132
+ # * text-align
133
+ # * padding
134
+ # * width
135
+ # * height
136
+ # * left
137
+ # * right
138
+ def css=(expressions)
139
+
140
+ values = { :container => [], :shadow => [], :text => [] }
141
+
142
+ expressions.to_s.delete("\n").split(";").each do |expression|
143
+
144
+ args = expression.split(":").each { |arg| arg.strip! }
145
+
146
+ if %w(width height padding left right).include?(args.first)
147
+ values[:container] << args
148
+ elsif args.first == "text-shadow"
149
+ values[:shadow] << args
150
+ else
151
+ values[:text] << args
152
+ end
153
+
154
+ end
155
+
156
+ @css ||= {}
157
+ @css.merge!(values) { |key, old, new|
158
+ keys = new.collect { |e| e.first }
159
+ old.each { |e| new << e unless keys.index(e.first) }
160
+ new
161
+ }
162
+
163
+ end
164
+
165
+ def canvas #:nodoc:
166
+ @canvas ||= proc {
167
+ padding = (get_css_property(css[:container], "padding") || text_width).to_i
168
+ width = (get_css_property(css[:container], "width") || text_width).to_i + padding
169
+ height = (get_css_property(css[:container], "height") || image.rows).to_i
170
+ Magick::Image.new(width, height) { self.background_color = "none" }
171
+ }.call
172
+ end
173
+
174
+ private
175
+
176
+ def draw
177
+ drawing = Magick::Draw.new
178
+ drawing.extend(CssMethods)
179
+ drawing.set_properties(css[:text])
180
+ margin_left = (get_css_property(css[:container], "left")).to_i
181
+ margin_right = (get_css_property(css[:container], "right")).to_i
182
+ margin = margin_left - margin_right
183
+ if !css[:shadow].empty?
184
+ args = css[:shadow].first.last.split
185
+ drawing.fill = args.first
186
+ drawing.annotate(self.canvas, 0, 0, margin + args[1].to_i, args[2].to_i, text)
187
+ drawing.fill = get_css_property(css[:text], "color")
188
+ end
189
+ drawing.annotate(self.canvas, 0, 0, margin, 0, text)
190
+ end
191
+
192
+ def get_css_property(stack, key)
193
+ stack.reject { |c| c.first != key }.flatten.last
194
+ end
195
+
196
+ def compose_button
197
+ main_image = image.crop(Magick::EastGravity, (canvas.columns - split_at), canvas.rows)
198
+ left_image = image.crop(Magick::WestGravity, split_at, canvas.rows)
199
+ self.canvas.composite!(main_image, left_image.columns, 0, Magick::OverCompositeOp)
200
+ self.canvas.composite!(left_image, Magick::WestGravity, Magick::OverCompositeOp)
201
+ end
202
+
203
+ def write_file(name)
204
+ FileUtils.mkdir_p destination_path unless destination_path.empty?
205
+ save_path = File.join(destination_path, name)
206
+ canvas.write(save_path)
207
+ save_path
208
+ end
209
+
210
+ def text_width(offset = 6)
211
+ temporary_canvas = Magick::Image.new(1000, 150)
212
+ drawing = Magick::Draw.new
213
+ drawing.extend(CssMethods)
214
+ drawing.set_properties(css[:text])
215
+
216
+ return (drawing.get_type_metrics(temporary_canvas, text)["width"] + offset)
217
+
218
+ end
219
+
220
+ end
@@ -0,0 +1,20 @@
1
+ spec = Gem::Specification.new do |spec|
2
+ spec.name = "magic_door"
3
+ spec.version = "0.1.0b"
4
+ spec.summary = "MagicDoor generator"
5
+ spec.description = <<-EOF
6
+ Generates custom button images with RMagick using the famous sliding doors css tehnique.
7
+ EOF
8
+
9
+ spec.authors << "Daniel Mircea"
10
+ spec.email = "daniel@viseztrance.com"
11
+ spec.homepage = "http://github.com/viseztrance/magic_door"
12
+
13
+ spec.files = Dir["{bin,lib,docs}/**/*"] + ["README.rdoc", "LICENSE", "Rakefile", "magic_door.gemspec"]
14
+ spec.executables = "magic-door"
15
+
16
+ spec.has_rdoc = true
17
+ spec.rdoc_options << "--main" << "README.rdoc" << "--title" << "MagicDoor generator" << "--line-numbers"
18
+ "--webcvs" << "http://github.com/viseztrance/magic_door"
19
+ spec.extra_rdoc_files = ["README.rdoc", "LICENSE"]
20
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: magic_door
3
+ version: !ruby/object:Gem::Version
4
+ hash: 3247241
5
+ prerelease: true
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0b
10
+ version: 0.1.0b
11
+ platform: ruby
12
+ authors:
13
+ - Daniel Mircea
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-19 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |
23
+ Generates custom button images with RMagick using the famous sliding doors css tehnique.
24
+
25
+ email: daniel@viseztrance.com
26
+ executables:
27
+ - magic-door
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README.rdoc
32
+ - LICENSE
33
+ files:
34
+ - bin/magic-door
35
+ - lib/magic_door.rb
36
+ - README.rdoc
37
+ - LICENSE
38
+ - Rakefile
39
+ - magic_door.gemspec
40
+ has_rdoc: true
41
+ homepage: http://github.com/viseztrance/magic_door
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --main
47
+ - README.rdoc
48
+ - --title
49
+ - MagicDoor generator
50
+ - --line-numbers
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">"
66
+ - !ruby/object:Gem::Version
67
+ hash: 25
68
+ segments:
69
+ - 1
70
+ - 3
71
+ - 1
72
+ version: 1.3.1
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.7
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: MagicDoor generator
80
+ test_files: []
81
+