amikula-sprite 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [Richard Huang (flyerhzm@gmail.com)]
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.md ADDED
@@ -0,0 +1,193 @@
1
+ # sprite #
2
+
3
+ `sprite` is a gem that helps generate css sprite images automagically. It's aim is to support all web frameworks (Merb/Rails/Sinatra), and have extensible output generator. By default, it supports CSS and SASS output (via mixins).
4
+
5
+ ## INSTALL ##
6
+
7
+ ### Install the `rmagick` gem ###
8
+
9
+ `sprite` currently requires the rmagick gem. to install it, use
10
+
11
+ gem install rmagick
12
+
13
+ if you have any problems with the rmagick gem, install imagemagick via macports first:
14
+
15
+ sudo port install libxml2
16
+ sudo port install ImageMagick
17
+
18
+ or via installer: http://github.com/maddox/magick-installer/tree/master
19
+
20
+ ### Install the `sprite` gem ###
21
+
22
+ Install the `sprite` gem from gemcutter
23
+
24
+ gem sources -a http://gemcutter.org
25
+ gem install sprite
26
+
27
+ ## USAGE ##
28
+
29
+ if installed as a gem, at your root project folder you can just run
30
+
31
+ sprite
32
+
33
+ ### Intelligent Defaults ###
34
+
35
+ Without having to configure anything, `sprite` will allow you to easily generate sprites based on a couple default folder settings we give you right off the bat.
36
+
37
+ For example, given you have the following setup:
38
+
39
+ public/
40
+ images/
41
+ sprites/
42
+ black-icons/
43
+ stop.png
44
+ go.png
45
+ back.png
46
+ forward.png
47
+
48
+ weather/
49
+ sunny.gif
50
+ rainy.gif
51
+ cloudy.gif
52
+
53
+ Running `sprite` with no configuration file will generate the following new files:
54
+
55
+ public/
56
+ stylesheets/
57
+ sprites.css
58
+ images/
59
+ sprites/
60
+ black-icons.png
61
+ weather.png
62
+
63
+ Any folders within `public/images/sprites/` will get compressed into a merged image file at the same
64
+ location. Then `sprites.css` will get generated in the stylesheets folder with all the class definitions for
65
+ these files. Just add a link to `sprites.css` into your html <head> and you're ready to go!
66
+
67
+
68
+ ## CONFIGURATION ##
69
+
70
+ Configuration of `sprite` is done via `config/sprite.yml`. It allows you to set sprite configuration options, and fine tune exactly which sprites get generated where.
71
+
72
+ * `config:` section defines all the global properties for sprite generation. Such as how it generates the styles, where it looks for images, where it writes it output file to, and what image file format it uses by default
73
+ - `style:` defines how the style rules are outputted. built in options are `css`, `sass`, and `sass_mixin`. (defaults to `css`)
74
+ - `style_output_path:` defines the file path where your style settings get written (defaults to `stylesheets/sprites`). the file extension not needed as it will be set based on the `style:` setting
75
+ - `image_output_path:` defines the folder path where the combined sprite images files are written (defaults to `images/sprites/`)
76
+ - `css_image_path:` defines the path used in css to find sprite image files (defaults to `/images/sprites/`)
77
+ - `image_source_path:` defines the folder where source image files are read from (defaults to `images/`)
78
+ - `public_path:` defines the root folder where static assets live (defaults to `public/`)
79
+ - `sprites_class:` defines the class name that gets added to all sprite stylesheet rules (defaults to `sprites`)
80
+ - `default_format:` defines the default file image format of the generated files. (defaults to `png`)
81
+ - `default_spacing:` defines the default pixel spacing between sprites (defaults to 0)
82
+ - `resize_to:` width and height (in the format "120x60") to resize all source images to before composition. (defaults to no nil, or no resizing)
83
+ - `class_separator:` used to generated the class name by separating the image name and sprite name (defaults to `-`)
84
+ - `add_datestamps`: whether or not to add datestamps to the generated background image urls. this will allow proper cache versioning (defaults to `true`)
85
+
86
+ * `images:` section provides an array of configurations which define which image files are built, and where they get their sprites from. each image setup provides the following config options:
87
+ - `name:` name of image (required)
88
+ - `sources:` defines a list of source image filenames to build the target image from (required). They are parsed by <code>Dir.glob</code>
89
+ - `align:` defines the composite gravity type, horizontal or vertical. (defaults to `vertical`)
90
+ - `spaced_by:` spacing (in pixels) between the combined images. (defaults to `0`)
91
+ - `resize_to:` width and height (in the format "120x60") to resize all source images to before composition. (defaults to no nil, or no resizing)
92
+ - `format:` define what image file format gets created (optional, uses `default_format` setting if not set)
93
+
94
+ All image and style paths should be set relative to the public folder (which is configurable via public_path setting).
95
+
96
+ ### Sample Configuration `config/sprite.yml` ###
97
+
98
+ # defines the base configuration options (file paths, etc, default style, etc)
99
+
100
+ config:
101
+ style: css
102
+ style_output_path: stylesheets/sprites
103
+ image_output_path: images/sprites/
104
+ image_source_path: images/
105
+ public_path: public/
106
+ sprites_class: 'sprites'
107
+ class_separator: '-'
108
+ default_format: png
109
+ default_spacing: 50
110
+
111
+ # defines what sprite collections get created
112
+ images:
113
+
114
+ # creates a public/images/sprites/blue_stars.png image with 4 sprites in it
115
+ - name: blue_stars
116
+ format: png
117
+ align: horizontal
118
+ spaced_by: 50
119
+ sources:
120
+ - icons/blue-stars/small.png
121
+ - icons/blue-stars/medium.png
122
+ - icons/blue-stars/large.png
123
+ - icons/blue-stars/xlarge.png
124
+
125
+ # creates a public/images/sprites/green-stars.jpg image with
126
+ # all the gif files contained within /images/icons/green-stars/
127
+ - name: green_stars
128
+ format: png
129
+ align: vertical
130
+ spaced_by: 50
131
+ sources:
132
+ - icons/green-stars/*.gif
133
+
134
+ ### Style Settings ###
135
+
136
+ By default, it will use with `style: css` and generate the file at `public/stylesheets/sprites.css`
137
+
138
+ .sprites.blue-stars-small {
139
+ background: url('/images/icons/blue-stars/small.png') no-repeat 0px 0px;
140
+ width: 12px;
141
+ height: 6px;
142
+ }
143
+ .sprites.blue-stars-medium {
144
+ background: url('/images/icons/blue-stars/medium.png') no-repeat 0px 6px;
145
+ width: 30px;
146
+ height: 15px;
147
+ }
148
+ .sprites.blue-stars-large {
149
+ background: url('/images/icons/blue-stars/large.png') no-repeat 0px 21px;
150
+ width: 60px;
151
+ height: 30px;
152
+ }
153
+ .sprites.blue-stars-xlarge {
154
+ background: url('/images/icons/blue-stars/xlarge.png') no-repeat 0px 96px;
155
+ width: 100px;
156
+ height: 75px;
157
+ }
158
+
159
+ We also support mixin syntax via `style: sass_mixin`. If set, it will generate a SASS mixin which you can use in order to mix in these sprites anywhere within your SASS stylesheets. For this option, set `style_output_path:` to `stylesheets/sass/_sprites` in order to generate the sass mixin file at `stylesheets/sass/_sprites.sass`
160
+
161
+ @import "sass/mixins/sprites.sass"
162
+
163
+ // you can then use your sprite like this
164
+ .largebluestar
165
+ +sprite("blue-stars", "large")
166
+
167
+ .mysmallbluestar
168
+ +sprite("blue-stars", "small")
169
+
170
+ Additional style generators are very easy to add. We have one for `style: sass` and `style: sass_ext`. The `sass_ext` style is a work in progress, as it's attempting to write the sprite data to yml and use a dynamic sass extension to provide the mixin. Eventually, if it works, this will be the default for `sass_mixin`
171
+
172
+ ## Framework Integration?? ##
173
+
174
+ `sprite` is provided as a command line helper. Deep web framework integration is not implemented at this time, however it shouldn't be needed. Just generate your sprites on your dev machine by running the command line, check in the resulting sprite images and stylesheets to your source control, and deploy!
175
+
176
+ You can also easily script it out via capistrano. You could also run `sprite` on application start, or just about anywhere. Let me know what limitations you run into.
177
+
178
+ ## ABOUT `sprite` ##
179
+
180
+ `sprite` was originally based off of Richard Huang's excellent Rails plugin: [css_sprite](http://github.com/flyerhzm/css_sprite)
181
+
182
+ Since then it's been rebuilt (with some reuse of the image generation code) to be a general purpose ruby executable, with hooks for merb/rails/sinatra
183
+
184
+
185
+ ## LICENSE ##
186
+
187
+ Released under the MIT License
188
+
189
+ ## COPYRIGHT ##
190
+
191
+ Copyright (c) 2009 Gist
192
+
193
+ Original Codebase Copyright (c) 2009 [Richard Huang]
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+ require 'jeweler'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :spec
7
+
8
+ desc "Run all specs in spec directory"
9
+ Spec::Rake::SpecTask.new(:spec) do |t|
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ namespace :spec do
14
+ desc "Run all specs in spec directory with RCov"
15
+ Spec::Rake::SpecTask.new(:rcov) do |t|
16
+ t.spec_files = FileList['spec/**/*_spec.rb']
17
+ t.rcov = true
18
+ t.rcov_opts = ["--exclude spec/*,gems/*,lib/tasks/*,app/analytics/application_metrics.rb"]
19
+ end
20
+ end
21
+
22
+
23
+
24
+ Jeweler::Tasks.new do |gemspec|
25
+ gemspec.name = "amikula-sprite"
26
+ gemspec.summary = "generate your css sprites automagically"
27
+ gemspec.description = "sprite is a rails/merb plugin that generates sprites for css, sass"
28
+ gemspec.email = "amikula@gmail.com"
29
+ gemspec.homepage = "http://github.com/amikula/sprite"
30
+ gemspec.authors = ["Jacques Crocker", "Alf Mikula"]
31
+ gemspec.files.exclude '.gitignore'
32
+
33
+ # removing test files and specs from the gem to save space
34
+ gemspec.files -= Dir.glob("spec/**/*")
35
+ gemspec.test_files = []
36
+ end
37
+ Jeweler::GemcutterTasks.new
38
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.8
data/bin/sprite ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # The compass command line utility
3
+
4
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'sprite'))
5
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'sprite', 'runner'))
6
+
7
+ command = Sprite::Runner.new(ARGV)
8
+ exit command.run!
data/lib/sprite.rb ADDED
@@ -0,0 +1,28 @@
1
+ module Sprite
2
+
3
+ # provides the root directory to use when reading and writing files
4
+ def self.root
5
+ @root ||= nil
6
+
7
+ # set the root to the framework setting (if not already set)
8
+ @root ||= begin
9
+ if defined?(Rails)
10
+ Rails.root
11
+ elsif defined?(Merb)
12
+ Merb.root
13
+ else
14
+ "."
15
+ end
16
+ end
17
+ @root
18
+ end
19
+ end
20
+
21
+ require 'sprite/builder'
22
+ require 'sprite/config'
23
+ require 'sprite/image_combiner'
24
+ require 'sprite/image_config'
25
+ require 'sprite/image_reader'
26
+ require 'sprite/image_resizer'
27
+ require 'sprite/image_writer'
28
+ require 'sprite/styles'
@@ -0,0 +1,185 @@
1
+ require 'fileutils'
2
+ module Sprite
3
+ class Builder
4
+ attr_reader :config
5
+ attr_reader :images
6
+
7
+ def self.from_config(path = nil)
8
+ results = Config.read_config(path)
9
+ new(results["config"], results["images"])
10
+ end
11
+
12
+ def initialize(config = nil, images = nil)
13
+ @config = config || {}
14
+ set_config_defaults
15
+
16
+ @images = images || []
17
+ if @images.empty?
18
+ @images = default_images
19
+ end
20
+ expand_image_paths
21
+
22
+ # initialize datestamp
23
+ @datestamp_query = "?#{Time.now.to_i}" if @config["add_datestamps"]
24
+
25
+ # initialize sprite files
26
+ @sprite_files = {}
27
+ end
28
+
29
+ def build
30
+ @sprite_files = {}
31
+
32
+ if images.size > 0
33
+ # create images
34
+ images.each do |image|
35
+ write_image(image)
36
+ end
37
+
38
+ if @sprite_files.values.length > 0
39
+ # write css
40
+ write_styles
41
+ end
42
+ end
43
+ end
44
+
45
+ # get the disk path for the style output file
46
+ def style_output_path(relative = false)
47
+ style = Styles.get(config["style"]).new(self)
48
+
49
+ path = config['style_output_path']
50
+ unless path.include?(".#{style.extension}")
51
+ path = "#{path}.#{style.extension}"
52
+ end
53
+ Config.new(config).public_path(path, relative)
54
+ end
55
+
56
+ def image_path(group)
57
+ image_info = images.detect{|image| image['name'] == group}
58
+ image_config = ImageConfig.new(image_info, config)
59
+ sprite_file = "#{image_config.name}.#{image_config.format}"
60
+ "#{config['css_image_path']}#{sprite_file}"
61
+ end
62
+
63
+ protected
64
+ def write_image(image_info)
65
+ results = []
66
+ image_config = ImageConfig.new(image_info, config)
67
+ sources = image_config.sources
68
+ return unless sources.length > 0
69
+
70
+ name = image_config.name
71
+ resizer = ImageResizer.new(image_config.resize_to)
72
+ combiner = ImageCombiner.new(image_config)
73
+
74
+ # Let's get the sprite started with the first image
75
+ first_image = ImageReader.read(sources.shift)
76
+ resizer.resize(first_image)
77
+
78
+ dest_image = first_image
79
+ results << combiner.image_properties(dest_image).merge(:x => 0, :y => 0, :group => name)
80
+
81
+ # Now let's add the rest of the images in turn
82
+ sources.each do |source|
83
+ source_image = ImageReader.read(source)
84
+ resizer.resize(source_image)
85
+ if image_config.horizontal_layout?
86
+ x = dest_image.columns + image_config.spaced_by
87
+ y = 0
88
+ align = "horizontal"
89
+ else
90
+ x = 0
91
+ y = dest_image.rows + image_config.spaced_by
92
+ align = "vertical"
93
+ end
94
+ results << combiner.image_properties(source_image).merge(:x => -x, :y => -y, :group => name, :align => align)
95
+ dest_image = combiner.composite_images(dest_image, source_image, x, y)
96
+ end
97
+
98
+ ImageWriter.new(config).write(dest_image, name, image_config.format, image_config.quality, image_config.background_color)
99
+
100
+ @sprite_files["#{name}.#{image_config.format}#{@datestamp_query}"] = results
101
+ end
102
+
103
+ def write_styles
104
+ # use the absolute style output path to make sure we have the directory set up
105
+ path = style_output_path
106
+ FileUtils.mkdir_p(File.dirname(path))
107
+
108
+ # send the style the relative path
109
+ style.write(style_output_path(true), @sprite_files)
110
+ end
111
+
112
+ def style
113
+ @style ||= Styles.get(config["style"]).new(self)
114
+ end
115
+
116
+ # sets all the default values on the config
117
+ def set_config_defaults
118
+ @config['style'] ||= 'css'
119
+ @config['style_output_path'] ||= 'stylesheets/sprites'
120
+ @config['image_output_path'] ||= 'images/sprites/'
121
+ @config['css_image_path'] ||= "/#{@config['image_output_path']}"
122
+ @config['image_source_path'] ||= 'images/'
123
+ @config['public_path'] ||= 'public/'
124
+ @config['default_format'] ||= 'png'
125
+ @config['class_separator'] ||= '-'
126
+ @config["sprites_class"] ||= 'sprites'
127
+ @config["default_spacing"] ||= 0
128
+
129
+ unless @config.has_key?("add_datestamps")
130
+ @config["add_datestamps"] = true
131
+ end
132
+ end
133
+
134
+ # if no image configs are detected, set some intelligent defaults
135
+ def default_images
136
+ sprites_path = image_source_path("sprites")
137
+ collection = []
138
+
139
+ if File.exists?(sprites_path)
140
+ Dir.glob(File.join(sprites_path, "*")) do |dir|
141
+ next unless File.directory?(dir)
142
+ source_name = File.basename(dir)
143
+
144
+ # default to finding all png, gif, jpg, and jpegs within the directory
145
+ collection << {
146
+ "name" => source_name,
147
+ "sources" => [
148
+ File.join("sprites", source_name, "*.png"),
149
+ File.join("sprites", source_name, "*.gif"),
150
+ File.join("sprites", source_name, "*.jpg"),
151
+ File.join("sprites", source_name, "*.jpeg"),
152
+ ]
153
+ }
154
+ end
155
+ end
156
+ collection
157
+ end
158
+
159
+ # expands out sources, taking the Glob paths and turning them into separate entries in the array
160
+ def expand_image_paths
161
+ # cycle through image sources and expand out globs
162
+ @images.each do |image|
163
+ # expand out all the globs
164
+ image['sources'] = image['sources'].to_a.map{ |source|
165
+ Dir.glob(image_source_path(source))
166
+ }.flatten.compact
167
+ end
168
+ end
169
+
170
+ # get the disk path for an image source file
171
+ def image_source_path(location, relative = false)
172
+ path_parts = []
173
+ path_parts << Config.chop_trailing_slash(config["image_source_path"]) if Config.path_present?(config['image_source_path'])
174
+ path_parts << location
175
+ Config.new(config).public_path(File.join(*path_parts), relative)
176
+ end
177
+
178
+ def style_template_source_path(image, relative = false)
179
+ location = image["style_output_template"]
180
+ path_parts = []
181
+ path_parts << location
182
+ Config.new(config).public_path(File.join(*path_parts), relative)
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,48 @@
1
+ module Sprite
2
+ class Config
3
+ DEFAULT_CONFIG_PATH = 'config/sprite.yml'
4
+
5
+ def self.read_config(path = nil)
6
+ config_path = File.join(Sprite.root, path || DEFAULT_CONFIG_PATH)
7
+
8
+ # read configuration
9
+ if File.exists?(config_path)
10
+ begin
11
+ File.open(config_path) {|f| YAML::load(f)} || {}
12
+ rescue => e
13
+ puts "Error reading sprite config: #{config_path}"
14
+ puts e.to_s
15
+ {}
16
+ end
17
+ else
18
+ {}
19
+ end
20
+ end
21
+
22
+ # chop off the trailing slash on a directory path (if it exists)
23
+ def self.chop_trailing_slash(path)
24
+ path = path[0...-1] if path[-1] == File::SEPARATOR
25
+ path
26
+ end
27
+
28
+ # check if the path is set
29
+ def self.path_present?(path)
30
+ path.to_s.strip != ""
31
+ end
32
+
33
+ def initialize(settings_hash)
34
+ @settings = settings_hash
35
+ end
36
+
37
+ # get the disk path for a location within the public folder (if set)
38
+ def public_path(location, relative = false)
39
+ path_parts = []
40
+ path_parts << Sprite.root unless relative
41
+ path_parts << Config.chop_trailing_slash(@settings['public_path']) if Config.path_present?(@settings['public_path'])
42
+ path_parts << location
43
+
44
+ File.join(*path_parts)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ module Sprite
2
+ class ImageCombiner
3
+ def initialize(image_config)
4
+ # avoid loading rmagick till the last possible moment
5
+ require 'rmagick'
6
+ @image_config = image_config
7
+ end
8
+
9
+ def composite_images(dest_image, src_image, x, y)
10
+ width = [src_image.columns + x, dest_image.columns].max
11
+ height = [src_image.rows + y, dest_image.rows].max
12
+ image = Magick::Image.new(width, height)
13
+ if @image_config.background_color
14
+ image.opacity = 0
15
+ else
16
+ image.opacity = Magick::MaxRGB
17
+ end
18
+
19
+ image.composite!(dest_image, 0, 0, Magick::OverCompositeOp)
20
+ image.composite!(src_image, x, y, Magick::OverCompositeOp)
21
+ image
22
+ end
23
+
24
+ # Image Utility Methods
25
+
26
+ def image_properties(image)
27
+ {:name => File.basename(image.filename).split('.')[0], :width => image.columns, :height => image.rows}
28
+ end
29
+
30
+ # REMOVE RMAGICK AND USE IMAGEMAGICK FROM THE COMMAND LINE
31
+ # identify => find properties for an image
32
+ # composite => combine 2 images
33
+
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ module Sprite
2
+ class ImageConfig
3
+ def initialize(image_info, global_config_info)
4
+ @image_info = image_info
5
+ @global_config_info = global_config_info
6
+ end
7
+
8
+ def sources
9
+ @image_info['sources'].to_a
10
+ end
11
+
12
+ def name
13
+ @image_info['name']
14
+ end
15
+
16
+ def format
17
+ @image_info['format'] || @global_config_info["default_format"]
18
+ end
19
+
20
+ def quality
21
+ @image_info['quality'] || @global_config_info["default_quality"]
22
+ end
23
+
24
+ def background_color
25
+ @image_info['background_color'] || @global_config_info["default_background_color"]
26
+ end
27
+
28
+ def spaced_by
29
+ @image_info['spaced_by'] || @global_config_info["default_spacing"] || 0
30
+ end
31
+
32
+ def resize_to
33
+ @image_info['resize_to'] || @global_config_info['resize_to']
34
+ end
35
+
36
+ def horizontal_layout?
37
+ @image_info['align'].to_s == 'horizontal'
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ module Sprite
2
+ class ImageReader
3
+ def self.read(image_filename)
4
+ # avoid loading rmagick till the last possible moment
5
+ require 'rmagick'
6
+
7
+ Magick::Image::read(image_filename).first
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ module Sprite
2
+ class ImageResizer
3
+ def initialize(resize_to)
4
+ if resize_to
5
+ @resizing = true
6
+ @target_width, @target_height = *(resize_to.split('x').map(&:to_i))
7
+ end
8
+ end
9
+
10
+ def resize(image)
11
+ if @resizing
12
+ needs_resizing = image.columns != @target_width || image.rows != @target_height
13
+ if needs_resizing
14
+ image.scale!(@target_width, @target_height)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Sprite
2
+ class ImageWriter
3
+ def initialize(config)
4
+ @config = config
5
+ end
6
+
7
+ def write(image, name, format, quality = nil, background_color = nil)
8
+ # set up path
9
+ path = image_output_path(name, format)
10
+ FileUtils.mkdir_p(File.dirname(path))
11
+
12
+ # write sprite image file to disk
13
+ image.write(path) {
14
+ self.quality = quality unless quality.nil?
15
+ self.background_color = background_color unless background_color.nil?
16
+ }
17
+ end
18
+
19
+ # get the disk path for a location within the image output folder
20
+ def image_output_path(name, format, relative = false)
21
+ path_parts = []
22
+ path_parts << Config.chop_trailing_slash(@config['image_output_path']) if Config.path_present?(@config['image_output_path'])
23
+ path_parts << "#{name}.#{format}"
24
+ Config.new(@config).public_path(File.join(*path_parts), relative)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ module Sprite
2
+ class Runner
3
+
4
+ attr_accessor :options
5
+ def initialize(args)
6
+ self.options = set_options(args)
7
+ end
8
+
9
+ def set_options(args)
10
+ opts = {}
11
+ # TODO
12
+ # edit options with passed in args
13
+ opts[:path] = args.first if args.any?
14
+
15
+ opts
16
+ end
17
+
18
+ # run sprite creator
19
+ def run!
20
+ begin
21
+ Sprite::Builder.from_config(options[:path]).build
22
+ # rescue Exception => e
23
+ # # catch errors
24
+ # puts "ERROR"
25
+ # puts e
26
+ # return 1
27
+ end
28
+ 0
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,81 @@
1
+ module Sprite
2
+ module Sass
3
+ module Extensions
4
+ def sprite_background(group, image)
5
+ sprite = sprite_data(group, image)
6
+ if sprite
7
+ sprite_path = sprite_builder.image_path(group.value)
8
+ "url('#{sprite_path}') no-repeat #{sprite[:x]}px #{sprite[:y]}px"
9
+ else
10
+ ""
11
+ end
12
+ end
13
+
14
+ def sprite_width(*args)
15
+ sprite_attr(:width, *args)
16
+ end
17
+
18
+ def sprite_height(*args)
19
+ sprite_attr(:height, *args)
20
+ end
21
+
22
+ def sprite_x_offset(*args)
23
+ sprite_attr(:x, *args)
24
+ end
25
+
26
+ def sprite_y_offset(*args)
27
+ sprite_attr(:y, *args)
28
+ end
29
+
30
+ def sprite_image(group)
31
+ sprite_builder.image_path(group.value)
32
+ end
33
+
34
+ def sprite_url(group)
35
+ "url('#{sprite_image(group)}')"
36
+ end
37
+
38
+ def sprite_offset(group, image, x=nil, y=nil)
39
+ x ||= sprite_x_offset(group, image)
40
+ y ||= sprite_y_offset(group, image)
41
+ "#{x} #{y}"
42
+ end
43
+
44
+ protected
45
+ def sprite_attr(attr, group, image)
46
+ sprite = sprite_data(group, image)
47
+ if sprite
48
+ "#{sprite[attr]}px"
49
+ else
50
+ ""
51
+ end
52
+ end
53
+
54
+ def sprite_builder
55
+ @__sprite_builder ||= Builder.from_config
56
+ end
57
+
58
+ def sprite_data(group, image)
59
+ unless @__sprite_data
60
+ sprite_data_path = sprite_builder.style_output_path
61
+
62
+ # read sprite data from yml
63
+ @__sprite_data = File.open(sprite_data_path) { |yf| YAML::load( yf ) }
64
+ end
65
+
66
+ group_data = @__sprite_data[group.to_s]
67
+ if group_data
68
+ return group_data[image.to_s]
69
+ else
70
+ nil
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ if defined?(Sass)
78
+ module Sass::Script::Functions
79
+ include Sprite::Sass::Extensions
80
+ end
81
+ end
@@ -0,0 +1,22 @@
1
+ require 'sprite/styles/sass_generator'
2
+ require 'sprite/styles/css_generator'
3
+ require 'sprite/styles/sass_yml_generator'
4
+ require 'sprite/styles/sass_mixin_generator'
5
+ require 'sprite/styles/templated_css_generator'
6
+
7
+ module Sprite::Styles
8
+ GENERATORS = {
9
+ "css" => "CssGenerator",
10
+ "templated_css" => "TemplatedCssGenerator",
11
+ "sass" => "SassGenerator",
12
+ "sass_mixin" => "SassMixinGenerator",
13
+ "sass_yml" => "SassYmlGenerator"
14
+ }
15
+
16
+ def self.get(config)
17
+ const_get(GENERATORS[config])
18
+ rescue
19
+ CssGenerator
20
+ end
21
+
22
+ end
@@ -0,0 +1,36 @@
1
+ module Sprite
2
+ module Styles
3
+ # renders standard css style rules
4
+ class CssGenerator
5
+ def initialize(builder)
6
+ @builder = builder
7
+ end
8
+
9
+ def write(path, sprite_files)
10
+ # write styles to disk
11
+ File.open(File.join(Sprite.root, path), 'w') do |f|
12
+ write_standard_css(f, sprite_files)
13
+ end
14
+ end
15
+
16
+ def write_standard_css(f, sprite_files)
17
+ # set up class_name to append to each rule
18
+ sprites_class = @builder.config['sprites_class'] ? ".#{@builder.config['sprites_class']}" : ""
19
+
20
+ sprite_files.each do |sprite_file, sprites|
21
+ sprites.each do |sprite|
22
+ f.puts "#{sprites_class}.#{sprite[:group]}#{@builder.config['class_separator']}#{sprite[:name]} {"
23
+ f.puts " background: url('/#{@builder.config['image_output_path']}#{sprite_file}') no-repeat #{sprite[:x]}px #{sprite[:y]}px;"
24
+ f.puts " width: #{sprite[:width]}px;"
25
+ f.puts " height: #{sprite[:height]}px;"
26
+ f.puts "}"
27
+ end
28
+ end
29
+ end
30
+
31
+ def extension
32
+ "css"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ module Sprite
2
+ module Styles
3
+
4
+ # renders standard sass rules
5
+ class SassGenerator
6
+ def initialize(builder)
7
+ @builder = builder
8
+ end
9
+
10
+ def write(path, sprite_files)
11
+ @level = 0
12
+
13
+ File.open(File.join(Sprite.root, path), 'w') do |f|
14
+ if @builder.config['sprites_class']
15
+ f.puts ".#{@builder.config['sprites_class']}"
16
+ @level += 1
17
+ end
18
+
19
+ sprite_files.each do |sprite_file, sprites|
20
+ sprites.each do |sprite|
21
+ f.puts sass_line("&.#{sprite[:group]}#{@builder.config['class_separator']}#{sprite[:name]}")
22
+ @level += 1
23
+ f.puts sass_line("background: url('/#{@builder.config['image_output_path']}#{sprite_file}') no-repeat #{sprite[:x]}px #{sprite[:y]}px")
24
+ f.puts sass_line("width: #{sprite[:width]}px")
25
+ f.puts sass_line("height: #{sprite[:height]}px")
26
+ f.puts sass_line("")
27
+ @level -= 1
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ # write sass output with correct tab spaces prepended
34
+ def sass_line(sass)
35
+ "#{' '*@level}#{sass}"
36
+ end
37
+
38
+ def extension
39
+ "sass"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,46 @@
1
+ module Sprite
2
+ module Styles
3
+ # renders a yml file that is later parsed by a sass extension when generating the mixins
4
+ class SassMixinGenerator
5
+ def initialize(builder)
6
+ @builder = builder
7
+ end
8
+
9
+ def write(path, sprite_files)
10
+ # write the sass mixins to disk
11
+ File.open(File.join(Sprite.root, path), 'w') do |f|
12
+ add_else = false
13
+
14
+ f.puts "= sprite(!group_name, !image_name, !offset=0)"
15
+ sprite_files.each do |sprite_file, sprites|
16
+ sprites.each do |sprite|
17
+
18
+ f << " @"
19
+ if add_else
20
+ f << "else "
21
+ end
22
+ add_else = true
23
+ #{sprite[:x]}px #{sprite[:y]}px
24
+
25
+ if sprite[:align] == 'horizontal'
26
+ background_offset = "\#{#{sprite[:x]}+!offset}px #{sprite[:y]}px"
27
+ else
28
+ background_offset = "#{sprite[:x]}px \#{#{sprite[:y]}+!offset}px"
29
+ end
30
+
31
+ f.puts %{if !group_name == "#{sprite[:group]}" and !image_name == "#{sprite[:name]}"}
32
+ f.puts " background: url('/#{@builder.config['image_output_path']}#{sprite_file}') no-repeat #{background_offset}"
33
+ f.puts " width: #{sprite[:width]}px"
34
+ f.puts " height: #{sprite[:height]}px"
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def extension
41
+ "sass"
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,53 @@
1
+ require 'yaml'
2
+ module Sprite
3
+ module Styles
4
+ # renders a yml file that is later parsed by a sass extension when generating the mixins
5
+ class SassYmlGenerator
6
+ def initialize(builder)
7
+ @builder = builder
8
+ end
9
+
10
+ def write(path, sprite_files)
11
+ # build the yml file
12
+ write_config(path, sprite_files)
13
+
14
+ # Where to put the sass mixin file
15
+ sass_path = path.gsub(".yml", ".sass")
16
+
17
+ # write the sass mixins to disk
18
+ File.open(File.join(Sprite.root, sass_path), 'w') do |f|
19
+ f.puts "!sprite_data = '#{path}'"
20
+ f.puts ""
21
+ f.puts "= sprite(!group_name, !image_name)"
22
+ f.puts " background= sprite_background(!group_name, !image_name)"
23
+ f.puts " width= sprite_width(!group_name, !image_name)"
24
+ f.puts " height= sprite_height(!group_name, !image_name)"
25
+ f.puts ""
26
+ end
27
+ end
28
+
29
+ # write the sprite configuration file (used by the yml extension)
30
+ def write_config(path, sprite_files)
31
+ # build a grouped hash with all the sprites in it
32
+ result = {}
33
+ sprite_files.each do |sprite_file, sprites|
34
+ sprites.each do |sprite|
35
+ if sprite[:group]
36
+ result[sprite[:group]] ||= {}
37
+ result[sprite[:group]][sprite[:name]] = sprite
38
+ end
39
+ end
40
+ end
41
+
42
+ # write the config yml to disk
43
+ File.open(File.join(Sprite.root, path), 'w') do |f|
44
+ YAML.dump(result, f)
45
+ end
46
+ end
47
+
48
+ def extension
49
+ "yml"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,41 @@
1
+ require 'erb'
2
+
3
+ module Sprite
4
+ module Styles
5
+ # renders css rules from template
6
+ class TemplatedCssGenerator
7
+ def initialize(builder)
8
+ @builder = builder
9
+ end
10
+
11
+ def write(path, sprite_files)
12
+ # write styles to disk
13
+ File.open(File.join(Sprite.root, path), 'w') do |f|
14
+ f.puts "/* Generated by the sprite gem */"
15
+ sprite_files.each do |sprite_file|
16
+ @builder.images.each do |image|
17
+ if "#{image['name']}.#{image['format']}" == sprite_file[0]
18
+ erb_path = @builder.send :style_template_source_path, image
19
+ erb_template = ERB.new(File.read(erb_path))
20
+ sprites = sprite_file[1]
21
+ sprites.each do |sprite|
22
+ name = sprite[:name]
23
+ width = sprite[:width]
24
+ height = sprite[:height]
25
+ left = sprite[:x]
26
+ top = sprite[:y]
27
+ image_path = ImageWriter.new(@builder.config).image_output_path(image['name'], image['format'])
28
+ f.puts erb_template.result(binding)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def extension
37
+ "css"
38
+ end
39
+ end
40
+ end
41
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'sprite'
2
+
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), '../lib/sprite/sprite.rb')
2
+
3
+ namespace :sprite do
4
+ desc "build sprite images based on config/sprite.yml"
5
+ task :build do
6
+ Sprite.new.build
7
+ end
8
+
9
+ task :list => [:build] do
10
+ # TODO
11
+ # list all the currently configured sprites
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: amikula-sprite
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.8
5
+ platform: ruby
6
+ authors:
7
+ - Jacques Crocker
8
+ - Alf Mikula
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2010-03-14 00:00:00 -08:00
14
+ default_executable: sprite
15
+ dependencies: []
16
+
17
+ description: sprite is a rails/merb plugin that generates sprites for css, sass
18
+ email: amikula@gmail.com
19
+ executables:
20
+ - sprite
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README.md
25
+ files:
26
+ - MIT-LICENSE
27
+ - README.md
28
+ - Rakefile
29
+ - VERSION
30
+ - bin/sprite
31
+ - lib/sprite.rb
32
+ - lib/sprite/builder.rb
33
+ - lib/sprite/config.rb
34
+ - lib/sprite/image_combiner.rb
35
+ - lib/sprite/image_config.rb
36
+ - lib/sprite/image_reader.rb
37
+ - lib/sprite/image_resizer.rb
38
+ - lib/sprite/image_writer.rb
39
+ - lib/sprite/runner.rb
40
+ - lib/sprite/sass_extensions.rb
41
+ - lib/sprite/styles.rb
42
+ - lib/sprite/styles/css_generator.rb
43
+ - lib/sprite/styles/sass_generator.rb
44
+ - lib/sprite/styles/sass_mixin_generator.rb
45
+ - lib/sprite/styles/sass_yml_generator.rb
46
+ - lib/sprite/styles/templated_css_generator.rb
47
+ - rails/init.rb
48
+ - tasks/sprite_tasks.rake
49
+ has_rdoc: true
50
+ homepage: http://github.com/amikula/sprite
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --charset=UTF-8
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: generate your css sprites automagically
77
+ test_files: []
78
+