css-spriter 0.9.2

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.
Files changed (70) hide show
  1. data/.gitignore +12 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +81 -0
  4. data/Rakefile +45 -0
  5. data/VERSION +1 -0
  6. data/bin/css-spriter +45 -0
  7. data/bin/png_info +61 -0
  8. data/examples/filter_util.rb +17 -0
  9. data/examples/sprites/.mtimes +12 -0
  10. data/examples/sprites/README +5 -0
  11. data/examples/sprites/fragment.css +0 -0
  12. data/examples/sprites/index.html +26 -0
  13. data/examples/sprites/many_sized_cats/.mtimes +3 -0
  14. data/examples/sprites/many_sized_cats/cat-on-keyboard.png +0 -0
  15. data/examples/sprites/many_sized_cats/darth_cat.png +0 -0
  16. data/examples/sprites/many_sized_cats/fragment.css +21 -0
  17. data/examples/sprites/many_sized_cats/music-keyboard-cat.png +0 -0
  18. data/examples/sprites/many_sized_cats/sprite.css +21 -0
  19. data/examples/sprites/many_sized_cats/sprite.png +0 -0
  20. data/examples/sprites/server.rb +10 -0
  21. data/examples/sprites/sprite.css +49 -0
  22. data/examples/sprites/words/.mtimes +4 -0
  23. data/examples/sprites/words/fragment.css +28 -0
  24. data/examples/sprites/words/latitude.png +0 -0
  25. data/examples/sprites/words/of.png +0 -0
  26. data/examples/sprites/words/set.png +0 -0
  27. data/examples/sprites/words/specified.png +0 -0
  28. data/examples/sprites/words/sprite.css +28 -0
  29. data/examples/sprites/words/sprite.png +0 -0
  30. data/init.rb +1 -0
  31. data/lib/css-spriter.rb +16 -0
  32. data/lib/css-spriter/directory_processor.rb +94 -0
  33. data/lib/css-spriter/image_data.rb +128 -0
  34. data/lib/css-spriter/mtime_tracker.rb +79 -0
  35. data/lib/css-spriter/png/chunk.rb +12 -0
  36. data/lib/css-spriter/png/file_header.rb +7 -0
  37. data/lib/css-spriter/png/filters.rb +80 -0
  38. data/lib/css-spriter/png/idat.rb +26 -0
  39. data/lib/css-spriter/png/iend.rb +9 -0
  40. data/lib/css-spriter/png/ihdr.rb +34 -0
  41. data/lib/css-spriter/png/image.rb +153 -0
  42. data/lib/css-spriter/png/parser.rb +54 -0
  43. data/lib/css-spriter/processor.rb +28 -0
  44. data/lib/css-spriter/sprite.rb +39 -0
  45. data/lib/css-spriter/stylesheet_builder.rb +22 -0
  46. data/spec/builders/image_builder.rb +23 -0
  47. data/spec/css_fragments/deep/style/fragment.css +1 -0
  48. data/spec/css_fragments/some/fragment.css +1 -0
  49. data/spec/expected_output/merge_right_test.png +0 -0
  50. data/spec/expected_output/write_test.png +0 -0
  51. data/spec/image_data_spec.rb +63 -0
  52. data/spec/images/lightening.png +0 -0
  53. data/spec/integration_spec.rb +151 -0
  54. data/spec/lib/file_header_spec.rb +10 -0
  55. data/spec/lib/idat_spec.rb +31 -0
  56. data/spec/lib/ihdr_spec.rb +43 -0
  57. data/spec/lib/image_spec.rb +42 -0
  58. data/spec/lib/parser_spec.rb +12 -0
  59. data/spec/lib/sprite_spec.rb +39 -0
  60. data/spec/mtime_tracking_spec.rb +69 -0
  61. data/spec/spec.opts +1 -0
  62. data/spec/spec_helper.rb +17 -0
  63. data/spec/sprite_dirs/words/latitude.png +0 -0
  64. data/spec/sprite_dirs/words/of.png +0 -0
  65. data/spec/sprite_dirs/words/set.png +0 -0
  66. data/spec/sprite_dirs/words/specified.png +0 -0
  67. data/spec/tmp/merge_right_test.png +0 -0
  68. data/spec/tmp/write_test.png +0 -0
  69. data/tasks/spriter_tasks.rake +25 -0
  70. metadata +148 -0
Binary file
@@ -0,0 +1,151 @@
1
+ require 'benchmark'
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ describe 'PNG' do
5
+ before do
6
+ @img_dir = File.dirname(__FILE__) + '/images'
7
+ @expected_dir = File.dirname(__FILE__) + '/expected_output'
8
+ @tmp_dir = File.dirname(__FILE__) + '/tmp'
9
+ end
10
+
11
+ it 'can read and write a PNG' do
12
+ img = PNG::Image.open("#{@img_dir}/lightening.png")
13
+ img.write("#{@tmp_dir}/write_test.png", :filter_type => 0)
14
+ read("#{@expected_dir}/write_test.png").should == read("#{@tmp_dir}/write_test.png")
15
+ end
16
+
17
+ it 'can merge one PNG on the left of another' do
18
+ one = PNG::Image.image_data("#{@img_dir}/lightening.png", :rgba => false)
19
+ two = PNG::Image.image_data("#{@img_dir}/lightening.png", :rgba => false)
20
+ merged = one.merge_left two
21
+ PNG::Image.write("#{@tmp_dir}/merge_right_test.png", merged, :filter_type => 0)
22
+ read("#{@expected_dir}/merge_right_test.png").should == read("#{@tmp_dir}/merge_right_test.png")
23
+ end
24
+ end
25
+
26
+ describe "Dir sprite" do
27
+ before :all do
28
+ @dir = File.dirname(__FILE__) + "/sprite_dirs/words"
29
+ @spriter = DirectoryProcessor.new(@dir)
30
+ @sprite_file = @dir + "/sprite.png"
31
+ @css_file = @dir + "/fragment.css"
32
+ @spriter.write
33
+ end
34
+
35
+ after :all do
36
+ @spriter.cleanup
37
+ end
38
+
39
+ describe "Sprite generation" do
40
+ it "provides the correct dir name" do
41
+ @spriter.dir_name.should == 'words'
42
+ end
43
+
44
+ it "find all the pngs in a directory" do
45
+ expected = ['latitude.png', 'of.png', 'set.png', 'specified.png']
46
+ images = @spriter.images
47
+ images.map{|f| f.split('/').last}.should == expected
48
+ end
49
+
50
+ it "sprites all the images in a directory" do
51
+ File.exists?(@sprite_file).should be_true
52
+ end
53
+ end
54
+
55
+ describe "CSS fragments" do
56
+ before :all do
57
+ @template = @dir + "/template.css"
58
+ @css = @spriter.css
59
+ end
60
+
61
+ after do
62
+ File.delete(@template) rescue nil
63
+ end
64
+
65
+ it "should compose class names" do
66
+ @css.should include( ".words_latitude")
67
+ @css.should include( ".words_of" )
68
+ end
69
+
70
+ it "has the correct image path" do
71
+ @css.should include( "/sprite_dirs/words/sprite.png" )
72
+ end
73
+
74
+ it "should write css fragments for a sprite" do
75
+ File.exists?(@css_file).should be_true
76
+ end
77
+
78
+ it "can be overidden by including a template.css in the sprite directory" do
79
+ File.open(@template, 'w'){|f| f.write("override")}
80
+ @spriter.write
81
+ @spriter.css.should include("override")
82
+ end
83
+ end
84
+ end
85
+
86
+ describe 'Stylesheet generator' do
87
+ before :all do
88
+ @dir = File.dirname(__FILE__) + "/css_fragments"
89
+ @out = @dir + "/complete.css"
90
+ @builder = StylesheetBuilder.new(@dir)
91
+ @builder.output_file(@out)
92
+ @css = @builder.css
93
+ end
94
+
95
+ after :all do
96
+ @builder.cleanup
97
+ end
98
+
99
+ it "takes the css fragments and concatonates them into a single stylesheet" do
100
+ @css.should include( ".some_style" )
101
+ end
102
+
103
+ it "can handle nested folder structures" do
104
+ @css.should include( ".deep" )
105
+ end
106
+
107
+ it "writes the css file to the specified location" do
108
+ @builder.write
109
+ File.exists?(@out).should be_true
110
+ end
111
+ end
112
+
113
+ describe "Complete spriting process" do
114
+ before :all do
115
+ @dir = File.dirname(__FILE__) + "/sprite_dirs"
116
+ @css_file = @dir + "/sprite.css"
117
+ @spriter = CssSpriter::Processor.new(:path_prefix => "/images", :source => @dir, :css_file => @css_file)
118
+ @spriter.write
119
+ end
120
+
121
+ after :all do
122
+ @spriter.cleanup
123
+ #making sure it cleans things up - shitty place for these
124
+ File.exists?(@css_file).should be_false
125
+ File.exists?(@dir + "/words/sprite.png").should be_false
126
+ end
127
+
128
+ it "prepends a path prefix to all sprites in the css file" do
129
+ file = read(@css_file)
130
+ file.should include("/images/sprite_dirs/words")
131
+ end
132
+
133
+ it "can find all the sprite directories" do
134
+ dirs = @spriter.directories.map{|d| d.split('/').last}
135
+ dirs.should include( "words" )
136
+ end
137
+
138
+ it "generates the css file at the appropriate location" do
139
+ File.exists?(@css_file).should be_true
140
+ end
141
+
142
+ it "creates sprites/css for all subfolders" do
143
+ File.exists?(@dir + "/words/sprite.png").should be_true
144
+ File.exists?(@dir + "/words/fragment.css").should be_true
145
+ end
146
+ end
147
+
148
+ def read(file_name)
149
+ File.read(file_name)
150
+ end
151
+
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe PNG::FileHeader do
4
+ # the pnd header provides sanity checks against most common file transfer errrors
5
+ it "outputs the PNG header" do
6
+ header = PNG::FileHeader.new.encode
7
+ header.should == [137, 80, 78, 71, 13, 10, 26, 10].pack("C*")
8
+ end
9
+ end
10
+
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe PNG::IDAT do
4
+ before :each do
5
+ # it's just "Hello, World!" encoded
6
+ @data = "x\234\363H\315\311\311\327Q\b\317/\312IQ\004\000\037\236\004j"
7
+ end
8
+
9
+ it "accepts compressed data" do
10
+ @idat = PNG::IDAT.new
11
+
12
+ @idat << @data
13
+
14
+ @idat.encode.should == @data
15
+ end
16
+
17
+ it "can chunk its self" do
18
+ @idat = PNG::IDAT.new
19
+ @idat << @data
20
+ @chunk = chunk( "IDAT", @idat.encode )
21
+
22
+ @idat.to_chunk.should == @chunk
23
+ end
24
+
25
+ it "accepts uncompressed data for it's constructor" do
26
+ @idat = PNG::IDAT.new( "Hello, World!".unpack("C*") )
27
+
28
+ @idat.encode.should == @data
29
+ end
30
+
31
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe PNG::IHDR do
4
+ before :each do
5
+ @width = 40
6
+ @height = 40
7
+ @bit_depth = 8
8
+ @color_type = 2
9
+
10
+ @raw = [@width, @height, @bit_depth, @color_type, 0, 0, 0].pack("N2C5")
11
+ @chunk = chunk( "IHDR", @raw )
12
+ end
13
+
14
+ it "pulls out the width from the ihdr block" do
15
+ @header = PNG::IHDR.new_from_raw( @raw )
16
+ @header.width.should == @width
17
+ end
18
+
19
+ it "pulls out the height from the ihdr block" do
20
+ @header = PNG::IHDR.new_from_raw( @raw )
21
+ @header.height.should == @height
22
+ end
23
+
24
+ it "pulls out the bit depth from the ihdr block" do
25
+ @header = PNG::IHDR.new_from_raw( @raw )
26
+ @header.depth.should == @bit_depth
27
+ end
28
+
29
+ it "pulls out the color type from the ihdr block" do
30
+ @header = PNG::IHDR.new_from_raw( @raw )
31
+ @header.color_type.should == @color_type
32
+ end
33
+
34
+ it "encodes it's self properly" do
35
+ @header = PNG::IHDR.new_from_raw( @raw )
36
+ @header.encode.should == @raw
37
+ end
38
+
39
+ it "should be able to make a header chunk" do
40
+ @header = PNG::IHDR.new_from_raw( @raw )
41
+ @header.to_chunk.should == @chunk
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe PNG::Image do
4
+ before :each do
5
+ @sprite = Sprite.new
6
+
7
+ @builder = ImageBuilder.new
8
+
9
+ # 0 is the filter type for the row, then an RGB triplet since builder defaults to color type 2
10
+ @image1 = @builder.build( :width => 1, :height => 1, :name => "image1", :data => [0,1,2,3] )
11
+ @image2 = @builder.build( :width => 1, :height => 1, :name => "image2", :data => [0,4,5,6] )
12
+ end
13
+
14
+ it "can merge left" do
15
+ result = @image1.to_image.merge_left @image2.to_image
16
+
17
+ result.should == [[4,5,6,1,2,3]]
18
+ end
19
+
20
+ it "can encode the rows with filter 0" do
21
+ @image1.filter_encoded_rows(0).should == [[0, 1, 2, 3]]
22
+ end
23
+
24
+
25
+ it "can encode the rows with filter 1" do
26
+ image = @builder.build( :width => 2, :height => 1, :name => "image1", :data => [0,1,2,3,4,5,6] )
27
+
28
+ # filter byte of 1
29
+ # first byte of pixel 2 - pixel 1 is 3
30
+ # second byte of pixel 2 - pixel 1 is 3.. etc
31
+ image.filter_encoded_rows(1).should == [[1, 1, 2, 3, 3, 3, 3]]
32
+ end
33
+
34
+ it "can encode the rows with filter 2" do
35
+ image = @builder.build( :width => 2, :height => 2, :name => "image1",
36
+ :data => [0,1,2,3,4,5,6,
37
+ 0,0,0,0,0,0,0])
38
+ result = image
39
+ # filter byte of 2
40
+ result.filter_encoded_rows(2).should == [[2, 1, 2, 3, 4, 5, 6], [2, 255, 254, 253, 252, 251, 250]]
41
+ end
42
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe PNG::Parser do
4
+ it "errors out when the file header is wrong" do
5
+ bad_header = [5, 80, 78, 71, 13, 10, 26, 10].pack("C*")
6
+ file = StringIO.new( bad_header)
7
+
8
+ lambda {
9
+ Parser.go!( file )
10
+ }.should raise_error
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Sprite do
4
+ before :each do
5
+ @sprite = Sprite.new
6
+ @builder = ImageBuilder.new
7
+
8
+ #TODO - We should just create ImageData objects here
9
+ @image1 = @builder.build( :width => 50, :height => 50, :name => "image1").to_image
10
+ @image2 = @builder.build( :width => 50, :height => 50, :name => "image2").to_image
11
+ end
12
+
13
+ it "can merge an image to the right" do
14
+ @sprite.append( @image1 )
15
+ @sprite.append( @image2 )
16
+
17
+ @sprite.images.should == [@image1, @image2]
18
+ end
19
+
20
+ it "knows the location of each image in the sprite" do
21
+ @sprite.append( @image1 )
22
+ @sprite.append( @image2 )
23
+
24
+ @sprite.locations[@image1.name.to_sym].should == {:x => -( 0 ), :width=> @image1.width, :height => @image1.height }
25
+ @sprite.locations[@image2.name.to_sym].should == {:x => -( @image2.width ), :width=> @image2.width, :height => @image2.height }
26
+ end
27
+
28
+ it "knows the height of the tallest image" do
29
+ max_height = 70
30
+
31
+ @image3 = @builder.build( :width => 50, :height => max_height, :name => "image3")
32
+
33
+ @sprite.append( @image1 )
34
+ @sprite.append( @image3 )
35
+
36
+ @sprite.max_height.should == max_height
37
+ end
38
+
39
+ end
@@ -0,0 +1,69 @@
1
+ require 'benchmark'
2
+ require 'fileutils'
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
+
5
+ describe MtimeTracker do
6
+ describe "on a new directory" do
7
+ before do
8
+ @img_dir = File.dirname(__FILE__) + '/images'
9
+ @tracker = MtimeTracker.new(@img_dir)
10
+ end
11
+
12
+ after do
13
+ File.delete(@img_dir + "/.mtimes") rescue nil
14
+ end
15
+
16
+ it "tells me there are changes" do
17
+ @tracker.has_changes?.should be_true
18
+ end
19
+
20
+ it "tells me things have not changed after I update the tracking" do
21
+ @tracker.update
22
+ @tracker.has_changes?.should be_false
23
+ end
24
+ end
25
+
26
+ describe "on an existing directory" do
27
+ before do
28
+ @img_dir = File.dirname(__FILE__) + '/images'
29
+ MtimeTracker.new(@img_dir).update
30
+ @tracker = MtimeTracker.new(@img_dir)
31
+ end
32
+
33
+ after do
34
+ File.delete(@img_dir + "/.mtimes") rescue nil
35
+ end
36
+
37
+ it "tells me nothing has changed" do
38
+ @tracker.has_changes?.should be_false
39
+ end
40
+
41
+ describe "when a file has changed" do
42
+ before do
43
+ FileUtils.touch(@img_dir + "/lightening.png")
44
+ @tracker.reset
45
+ end
46
+
47
+ it "returns true from has_changes" do
48
+ @tracker.has_changes?.should be_true
49
+ @tracker.changeset.first.should include("/spec/images/lightening.png")
50
+ end
51
+ end
52
+
53
+ describe "file exclustions" do
54
+ before do
55
+ @img_dir = File.dirname(__FILE__) + '/images'
56
+ @tracker = MtimeTracker.new(@img_dir, :exclude => [/lightening/, "tacos"])
57
+ end
58
+
59
+ after do
60
+ File.delete(@img_dir + "/.mtimes") rescue nil
61
+ end
62
+
63
+ it "does not report excluded files as changed" do
64
+ FileUtils.touch(@img_dir + "/lightening.png")
65
+ @tracker.has_changes?.should be_false
66
+ end
67
+ end
68
+ end
69
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,17 @@
1
+ # $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ # $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'spec'
4
+ require 'spec/autorun'
5
+
6
+ require 'css-spriter'
7
+
8
+ require 'builders/image_builder'
9
+
10
+ Spec::Runner.configure do |config|
11
+
12
+ end
13
+
14
+ def chunk(type, data)
15
+ to_check = type + data
16
+ [data.length].pack("N") + to_check + [Zlib.crc32(to_check)].pack("N")
17
+ end
Binary file
Binary file
Binary file
Binary file
Binary file