magic_door 0.1.0b

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/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
+