sprite 0.1.7 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/Gemfile.lock +50 -0
- data/README.md +27 -23
- data/Rakefile +37 -20
- data/lib/sprite.rb +5 -0
- data/lib/sprite/builder.rb +90 -101
- data/lib/sprite/config.rb +48 -0
- data/lib/sprite/image_combiner.rb +17 -10
- data/lib/sprite/image_config.rb +40 -0
- data/lib/sprite/image_reader.rb +14 -0
- data/lib/sprite/image_resizer.rb +19 -0
- data/lib/sprite/image_writer.rb +29 -0
- data/lib/sprite/runner.rb +6 -4
- data/lib/sprite/sass_extensions.rb +93 -48
- data/lib/sprite/styles.rb +3 -1
- data/lib/sprite/styles/css_generator.rb +19 -15
- data/lib/sprite/styles/sass_generator.rb +5 -4
- data/lib/sprite/styles/sass_mixin_generator.rb +7 -7
- data/lib/sprite/styles/sass_yml_generator.rb +15 -16
- data/lib/sprite/styles/templated_css_generator.rb +41 -0
- data/spec/output/android_horizontal/images/sprites/android-icons.png +0 -0
- data/spec/output/android_horizontal/stylesheets/android-icons.css +1 -0
- data/spec/output/android_vertical/images/sprites/android-icons.png +0 -0
- data/spec/output/android_vertical/stylesheets/android-icons.css +1 -0
- data/spec/resources/android_css.erb +5 -0
- data/spec/resources/configs/android-icons.yml +19 -0
- data/spec/resources/configs/config-test.yml +26 -0
- data/spec/resources/images/android-icons/LICENSE +1 -0
- data/spec/resources/images/android-icons/barcode.png +0 -0
- data/spec/resources/images/android-icons/cards.png +0 -0
- data/spec/resources/images/android-icons/chart.png +0 -0
- data/spec/resources/images/android-icons/clock.png +0 -0
- data/spec/resources/images/android-icons/cloud.png +0 -0
- data/spec/resources/images/android-icons/colour-picker.png +0 -0
- data/spec/resources/images/android-icons/dialog.png +0 -0
- data/spec/resources/images/android-icons/dice.png +0 -0
- data/spec/resources/images/android-icons/disc.png +0 -0
- data/spec/resources/images/android-icons/equalizer.png +0 -0
- data/spec/resources/images/android-icons/filter.png +0 -0
- data/spec/resources/images/android-icons/flag.png +0 -0
- data/spec/resources/images/android-icons/flash.png +0 -0
- data/spec/resources/images/android-icons/globe.png +0 -0
- data/spec/resources/images/android-icons/happy.png +0 -0
- data/spec/resources/images/android-icons/large-tiles.png +0 -0
- data/spec/resources/images/android-icons/light.png +0 -0
- data/spec/resources/images/android-icons/love.png +0 -0
- data/spec/resources/images/android-icons/magnet.png +0 -0
- data/spec/resources/images/android-icons/monitor.png +0 -0
- data/spec/resources/images/android-icons/music.png +0 -0
- data/spec/resources/images/android-icons/pie-chart.png +0 -0
- data/spec/resources/images/android-icons/ruler.png +0 -0
- data/spec/resources/images/android-icons/sad.png +0 -0
- data/spec/resources/images/android-icons/seal.png +0 -0
- data/spec/resources/images/android-icons/shopping.png +0 -0
- data/spec/resources/images/android-icons/small-tiles.png +0 -0
- data/spec/resources/images/android-icons/sun.png +0 -0
- data/spec/resources/images/android-icons/tag.png +0 -0
- data/spec/resources/images/android-icons/umbrella.png +0 -0
- data/spec/resources/images/topics/good-topic.gif +0 -0
- data/spec/resources/images/topics/mid-topic.gif +0 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/sprite/builder_spec.rb +76 -0
- data/spec/sprite/config_spec.rb +64 -0
- data/spec/sprite/image_combiner_spec.rb +40 -0
- data/spec/sprite/image_reader_spec.rb +16 -0
- data/spec/sprite/styles/css_spec.rb +0 -0
- data/spec/sprite/styles/sass_mixin_spec.rb +0 -0
- data/spec/sprite/styles/sass_spec.rb +0 -0
- data/sprite.gemspec +30 -49
- metadata +142 -21
- data/VERSION +0 -1
@@ -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
|
@@ -1,15 +1,25 @@
|
|
1
1
|
module Sprite
|
2
2
|
class ImageCombiner
|
3
|
-
def initialize
|
4
|
-
|
5
|
-
|
3
|
+
def initialize(image_config)
|
4
|
+
begin
|
5
|
+
# avoid loading rmagick till the last possible moment
|
6
|
+
require "RMagick"
|
7
|
+
rescue LoadError
|
8
|
+
require 'rmagick'
|
9
|
+
end
|
10
|
+
|
11
|
+
@image_config = image_config
|
6
12
|
end
|
7
|
-
|
13
|
+
|
8
14
|
def composite_images(dest_image, src_image, x, y)
|
9
15
|
width = [src_image.columns + x, dest_image.columns].max
|
10
16
|
height = [src_image.rows + y, dest_image.rows].max
|
11
17
|
image = Magick::Image.new(width, height)
|
12
|
-
|
18
|
+
if @image_config.background_color
|
19
|
+
image.opacity = 0
|
20
|
+
else
|
21
|
+
image.opacity = Magick::MaxRGB
|
22
|
+
end
|
13
23
|
|
14
24
|
image.composite!(dest_image, 0, 0, Magick::OverCompositeOp)
|
15
25
|
image.composite!(src_image, x, y, Magick::OverCompositeOp)
|
@@ -17,17 +27,14 @@ module Sprite
|
|
17
27
|
end
|
18
28
|
|
19
29
|
# Image Utility Methods
|
20
|
-
def get_image(image_filename)
|
21
|
-
image = Magick::Image::read(image_filename).first
|
22
|
-
end
|
23
30
|
|
24
31
|
def image_properties(image)
|
25
32
|
{:name => File.basename(image.filename).split('.')[0], :width => image.columns, :height => image.rows}
|
26
33
|
end
|
27
|
-
|
34
|
+
|
28
35
|
# REMOVE RMAGICK AND USE IMAGEMAGICK FROM THE COMMAND LINE
|
29
36
|
# identify => find properties for an image
|
30
37
|
# composite => combine 2 images
|
31
38
|
|
32
39
|
end
|
33
|
-
end
|
40
|
+
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,14 @@
|
|
1
|
+
module Sprite
|
2
|
+
class ImageReader
|
3
|
+
def self.read(image_filename)
|
4
|
+
# avoid loading rmagick till the last possible moment
|
5
|
+
begin
|
6
|
+
require "RMagick"
|
7
|
+
rescue LoadError
|
8
|
+
require 'rmagick'
|
9
|
+
end
|
10
|
+
|
11
|
+
Magick::Image::read(image_filename).first
|
12
|
+
end
|
13
|
+
end
|
14
|
+
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,29 @@
|
|
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
|
+
|
24
|
+
cache_buster = "-#{@config['cache_buster']}" if @config['cache_buster']
|
25
|
+
path_parts << "#{name}#{cache_buster}.#{format}"
|
26
|
+
Config.new(@config).public_path(File.join(*path_parts), relative)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/sprite/runner.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
module Sprite
|
2
2
|
class Runner
|
3
|
-
|
3
|
+
|
4
4
|
attr_accessor :options
|
5
5
|
def initialize(args)
|
6
6
|
self.options = set_options(args)
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def set_options(args)
|
10
10
|
opts = {}
|
11
11
|
# TODO
|
12
12
|
# edit options with passed in args
|
13
|
+
opts[:path] = args.first if args.any?
|
14
|
+
|
13
15
|
opts
|
14
16
|
end
|
15
|
-
|
17
|
+
|
16
18
|
# run sprite creator
|
17
19
|
def run!
|
18
20
|
begin
|
@@ -25,6 +27,6 @@ module Sprite
|
|
25
27
|
end
|
26
28
|
0
|
27
29
|
end
|
28
|
-
|
30
|
+
|
29
31
|
end
|
30
32
|
end
|
@@ -1,57 +1,102 @@
|
|
1
|
-
module Sprite
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
+
::Sass::Script::String.new "url('#{sprite_path}') no-repeat #{sprite[:x]}px #{sprite[:y]}px"
|
9
|
+
else
|
10
|
+
::Sass::Script::String.new ""
|
11
|
+
end
|
12
|
+
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
14
|
+
def sprite_width(*args)
|
15
|
+
::Sass::Script::String.new sprite_attr(:width, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def sprite_height(*args)
|
19
|
+
::Sass::Script::String.new sprite_attr(:height, *args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def sprite_x_offset(*args)
|
23
|
+
::Sass::Script::String.new sprite_attr(:x, *args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def sprite_y_offset(*args)
|
27
|
+
::Sass::Script::String.new sprite_attr(:y, *args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def sprite_image(group)
|
31
|
+
::Sass::Script::String.new sprite_builder.image_path(group.value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def sprite_url(group)
|
35
|
+
::Sass::Script::String.new "url('#{sprite_image(group)}')"
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Return a sprite offset for the given image. When the optional x and y values are passed,
|
40
|
+
# numeric values are treated as additional offsets to the sprite image offset. Any string
|
41
|
+
# values are treated as replacement offsets.
|
42
|
+
#
|
43
|
+
# Examples:
|
44
|
+
# * sprite_offset("common", "icon", "100%", 5) => offset is "100% x+5" where x is the x offset in the sprite
|
45
|
+
# * sprite_offset("common", "icon", "top", "right") => offset is "top right"
|
46
|
+
def sprite_offset(group, image, x=nil, y=nil)
|
47
|
+
xoff = compute_offset(group, image, :x, x)
|
48
|
+
yoff = compute_offset(group, image, :y, y)
|
49
|
+
::Sass::Script::String.new "#{xoff} #{yoff}"
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
def compute_offset(group, image, axis, offset)
|
54
|
+
if offset
|
55
|
+
val = offset.value
|
56
|
+
if val.is_a? Fixnum
|
57
|
+
(sprite_attr(axis, group, image).to_i + val).to_s + 'px'
|
58
|
+
else
|
59
|
+
val
|
60
|
+
end
|
61
|
+
else
|
62
|
+
sprite_attr(axis, group, image)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def sprite_attr(attr, group, image)
|
67
|
+
sprite = sprite_data(group, image)
|
68
|
+
if sprite
|
69
|
+
"#{sprite[attr]}px"
|
70
|
+
else
|
71
|
+
""
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def sprite_builder
|
76
|
+
@__sprite_builder ||= Builder.from_config
|
77
|
+
end
|
78
|
+
|
79
|
+
def sprite_data(group, image)
|
80
|
+
unless @__sprite_data
|
81
|
+
sprite_data_path = sprite_builder.style_output_path
|
82
|
+
|
83
|
+
# read sprite data from yml
|
84
|
+
@__sprite_data = File.open(sprite_data_path) { |yf| YAML::load( yf ) }
|
85
|
+
end
|
86
|
+
|
87
|
+
group_data = @__sprite_data[group.value]
|
88
|
+
if group_data
|
89
|
+
return group_data[image.value]
|
90
|
+
else
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
end
|
48
94
|
end
|
49
95
|
end
|
50
|
-
|
51
96
|
end
|
52
97
|
|
53
98
|
if defined?(Sass)
|
54
99
|
module Sass::Script::Functions
|
55
100
|
include Sprite::Sass::Extensions
|
56
101
|
end
|
57
|
-
end
|
102
|
+
end
|
data/lib/sprite/styles.rb
CHANGED
@@ -2,15 +2,17 @@ require 'sprite/styles/sass_generator'
|
|
2
2
|
require 'sprite/styles/css_generator'
|
3
3
|
require 'sprite/styles/sass_yml_generator'
|
4
4
|
require 'sprite/styles/sass_mixin_generator'
|
5
|
+
require 'sprite/styles/templated_css_generator'
|
5
6
|
|
6
7
|
module Sprite::Styles
|
7
8
|
GENERATORS = {
|
8
9
|
"css" => "CssGenerator",
|
10
|
+
"templated_css" => "TemplatedCssGenerator",
|
9
11
|
"sass" => "SassGenerator",
|
10
12
|
"sass_mixin" => "SassMixinGenerator",
|
11
13
|
"sass_yml" => "SassYmlGenerator"
|
12
14
|
}
|
13
|
-
|
15
|
+
|
14
16
|
def self.get(config)
|
15
17
|
const_get(GENERATORS[config])
|
16
18
|
rescue
|
@@ -5,26 +5,30 @@ module Sprite
|
|
5
5
|
def initialize(builder)
|
6
6
|
@builder = builder
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def write(path, sprite_files)
|
10
|
-
# set up class_name to append to each rule
|
11
|
-
sprites_class = @builder.config['sprites_class'] ? ".#{@builder.config['sprites_class']}" : ""
|
12
|
-
|
13
10
|
# write styles to disk
|
14
11
|
File.open(File.join(Sprite.root, path), 'w') do |f|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
+
background_url = @builder.background_url(sprite_file)
|
22
|
+
sprites.each do |sprite|
|
23
|
+
f.puts "#{sprites_class}.#{sprite[:group]}#{@builder.config['class_separator']}#{sprite[:name]} {"
|
24
|
+
f.puts " background: #{background_url} no-repeat #{sprite[:x]}px #{sprite[:y]}px;"
|
25
|
+
f.puts " width: #{sprite[:width]}px;"
|
26
|
+
f.puts " height: #{sprite[:height]}px;"
|
27
|
+
f.puts "}"
|
24
28
|
end
|
25
|
-
end
|
29
|
+
end
|
26
30
|
end
|
27
|
-
|
31
|
+
|
28
32
|
def extension
|
29
33
|
"css"
|
30
34
|
end
|