psd 0.4.2 → 1.0.0
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.
- checksums.yaml +4 -4
- data/README.md +0 -1
- data/lib/psd/color.rb +90 -92
- data/lib/psd/image_modes/greyscale.rb +0 -16
- data/lib/psd/layer_info/typetool.rb +23 -2
- data/lib/psd/layer_mask.rb +1 -1
- data/lib/psd/nodes/ancestry.rb +2 -2
- data/lib/psd/version.rb +1 -1
- data/psd.gemspec +1 -1
- data/spec/files/text.psd +0 -0
- data/spec/hierarchy_spec.rb +34 -34
- data/spec/identity_spec.rb +3 -3
- data/spec/parsing_spec.rb +42 -41
- data/spec/psd_spec.rb +6 -4
- data/spec/text_spec.rb +16 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e806c5a8d10b43d7bddb5bf2e0e9ca3843a579f
|
4
|
+
data.tar.gz: 5560fc10135e67e5d396a367c5ae737cf0f84c51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcac2c4003765bc89eaba409c48b860bcc6d49ec377953332dcfccbb58baecc9b7531d285a95cfeaff14c1f8102b5b8bfab341ae915b4270f67760d271abf186
|
7
|
+
data.tar.gz: c8656fe0d3ccb52301f09e40856a0f90d1ad7ebeba4ba2793a0cc3edef958fd57daa9e16a69034abf1ae416420126bd1e9e456da5ba69b7edc28d0483755c3cb
|
data/README.md
CHANGED
data/lib/psd/color.rb
CHANGED
@@ -4,122 +4,120 @@ class PSD
|
|
4
4
|
# value of some kind. This means that we have to do all the conversion ourselves
|
5
5
|
# for each color space.
|
6
6
|
class Color
|
7
|
-
instance_eval do
|
8
7
|
# This is a relic of libpsd that will likely go away in a future version. It
|
9
8
|
# stored the entire color value in a 32-bit address space for speed.
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
color_to_argb(color)
|
9
|
+
def self.color_space_to_argb(color_space, color_component)
|
10
|
+
color = case color_space
|
11
|
+
when 0
|
12
|
+
rgb_to_color *color_component
|
13
|
+
when 1
|
14
|
+
hsb_to_color color_component[0],
|
15
|
+
color_component[1] / 100.0, color_component[2] / 100.0
|
16
|
+
when 2
|
17
|
+
cmyk_to_color color_component[0] / 100.0,
|
18
|
+
color_component[1] / 100.0, color_component[2] / 100.0,
|
19
|
+
color_component[3] / 100.0
|
20
|
+
when 7
|
21
|
+
lab_to_color *color_component
|
22
|
+
else
|
23
|
+
0x00FFFFFF
|
28
24
|
end
|
29
25
|
|
30
|
-
|
31
|
-
|
32
|
-
(color) >> 24,
|
33
|
-
((color) & 0x00FF0000) >> 16,
|
34
|
-
((color) & 0x0000FF00) >> 8,
|
35
|
-
(color) & 0x000000FF
|
36
|
-
]
|
37
|
-
end
|
26
|
+
color_to_argb(color)
|
27
|
+
end
|
38
28
|
|
39
|
-
|
40
|
-
|
41
|
-
|
29
|
+
def self.color_to_argb(color)
|
30
|
+
[
|
31
|
+
(color) >> 24,
|
32
|
+
((color) & 0x00FF0000) >> 16,
|
33
|
+
((color) & 0x0000FF00) >> 8,
|
34
|
+
(color) & 0x000000FF
|
35
|
+
]
|
36
|
+
end
|
42
37
|
|
43
|
-
|
44
|
-
|
45
|
-
|
38
|
+
def self.rgb_to_color(*args)
|
39
|
+
argb_to_color(255, *args)
|
40
|
+
end
|
46
41
|
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
def self.argb_to_color(a, r, g, b)
|
43
|
+
(a << 24) | (r << 16) | (g << 8) | b
|
44
|
+
end
|
50
45
|
|
51
|
-
|
52
|
-
|
53
|
-
|
46
|
+
def self.hsb_to_color(*args)
|
47
|
+
ahsb_to_color(255, *args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.ahsb_to_color(alpha, hue, saturation, brightness)
|
51
|
+
if saturation == 0.0
|
52
|
+
b = g = r = (255 * brightness).to_i
|
53
|
+
else
|
54
|
+
if brightness <= 0.5
|
55
|
+
m2 = brightness * (1 + saturation)
|
54
56
|
else
|
55
|
-
|
56
|
-
m2 = brightness * (1 + saturation)
|
57
|
-
else
|
58
|
-
m2 = brightness + saturation - brightness * saturation
|
59
|
-
end
|
60
|
-
|
61
|
-
m1 = 2 * brightness - m2
|
62
|
-
r = hue_to_color(hue + 120, m1, m2)
|
63
|
-
g = hue_to_color(hue, m1, m2)
|
64
|
-
b = hue_to_color(hue - 120, m1, m2)
|
57
|
+
m2 = brightness + saturation - brightness * saturation
|
65
58
|
end
|
66
59
|
|
67
|
-
|
60
|
+
m1 = 2 * brightness - m2
|
61
|
+
r = hue_to_color(hue + 120, m1, m2)
|
62
|
+
g = hue_to_color(hue, m1, m2)
|
63
|
+
b = hue_to_color(hue - 120, m1, m2)
|
68
64
|
end
|
69
65
|
|
70
|
-
|
71
|
-
|
72
|
-
if hue < 60
|
73
|
-
v = m1 + (m2 - m1) * hue / 60
|
74
|
-
elsif hue < 180
|
75
|
-
v = m2
|
76
|
-
elsif hue < 240
|
77
|
-
v = m1 + (m2 - m1) * (240 - hue) / 60
|
78
|
-
else
|
79
|
-
v = m1
|
80
|
-
end
|
66
|
+
argb_to_color alpha, r, g, b
|
67
|
+
end
|
81
68
|
|
82
|
-
|
69
|
+
def self.hue_to_color(hue, m1, m2)
|
70
|
+
hue = (hue % 360).to_i
|
71
|
+
if hue < 60
|
72
|
+
v = m1 + (m2 - m1) * hue / 60
|
73
|
+
elsif hue < 180
|
74
|
+
v = m2
|
75
|
+
elsif hue < 240
|
76
|
+
v = m1 + (m2 - m1) * (240 - hue) / 60
|
77
|
+
else
|
78
|
+
v = m1
|
83
79
|
end
|
84
80
|
|
85
|
-
|
86
|
-
|
87
|
-
g = 1 - (m * (1 - k) + k) * 255
|
88
|
-
b = 1 - (y * (1 - k) + k) * 255
|
81
|
+
(v * 255).to_i
|
82
|
+
end
|
89
83
|
|
90
|
-
|
91
|
-
|
92
|
-
|
84
|
+
def self.cmyk_to_color(c, m, y, k)
|
85
|
+
r = 1 - (c * (1 - k) + k) * 255
|
86
|
+
g = 1 - (m * (1 - k) + k) * 255
|
87
|
+
b = 1 - (y * (1 - k) + k) * 255
|
93
88
|
|
94
|
-
|
95
|
-
|
89
|
+
r = [0, r, 255].sort[1]
|
90
|
+
g = [0, g, 255].sort[1]
|
91
|
+
b = [0, b, 255].sort[1]
|
96
92
|
|
97
|
-
|
98
|
-
|
99
|
-
end
|
93
|
+
rgb_to_color r, g, b
|
94
|
+
end
|
100
95
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
96
|
+
def self.lab_to_color(*args)
|
97
|
+
alab_to_color(255, *args)
|
98
|
+
end
|
105
99
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
100
|
+
def self.alab_to_color(alpha, l, a, b)
|
101
|
+
xyz = lab_to_xyz(l, a, b)
|
102
|
+
axyz_to_color alpha, xyz[:x], xyz[:y], xyz[:z]
|
103
|
+
end
|
110
104
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
105
|
+
def self.lab_to_xyz(l, a, b)
|
106
|
+
y = (l + 16) / 116
|
107
|
+
x = y + (a / 500)
|
108
|
+
z = y - (b / 200)
|
115
109
|
|
116
|
-
|
117
|
-
|
118
|
-
r: (65535 - (c * (255 - k) + (k << 8))) >> 8,
|
119
|
-
g: (65535 - (m * (255 - k) + (k << 8))) >> 8,
|
120
|
-
b: (65535 - (y * (255 - k) + (k << 8))) >> 8
|
121
|
-
}.map { |k, v| [k, Util.clamp(v, 0, 255)] }]
|
110
|
+
x, y, z = [x, y, z].map do |n|
|
111
|
+
n**3 > 0.008856 ? n**3 : (n - 16 / 116) / 7.787
|
122
112
|
end
|
123
113
|
end
|
114
|
+
|
115
|
+
def self.cmyk_to_rgb(c, m, y, k)
|
116
|
+
Hash[{
|
117
|
+
r: (65535 - (c * (255 - k) + (k << 8))) >> 8,
|
118
|
+
g: (65535 - (m * (255 - k) + (k << 8))) >> 8,
|
119
|
+
b: (65535 - (y * (255 - k) + (k << 8))) >> 8
|
120
|
+
}.map { |k, v| [k, Util.clamp(v, 0, 255)] }]
|
121
|
+
end
|
124
122
|
end
|
125
123
|
end
|
@@ -2,22 +2,6 @@ class PSD::Image::Mode
|
|
2
2
|
module Greyscale
|
3
3
|
private
|
4
4
|
|
5
|
-
def combine_greyscale8_channel
|
6
|
-
if channels == 2
|
7
|
-
# We have an alpha channel
|
8
|
-
@num_pixels.times do |i|
|
9
|
-
alpha = @channel_data[i]
|
10
|
-
grey = @channel_data[@channel_length + i]
|
11
|
-
|
12
|
-
@pixel_data.push grey, grey, grey, alpha
|
13
|
-
end
|
14
|
-
else
|
15
|
-
@num_pixels.times do |i|
|
16
|
-
@pixel_data.push *([@channel_data[i]] * 3), 255
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
5
|
def combine_greyscale_channel
|
22
6
|
if channels == 2
|
23
7
|
(0...@num_pixels).step(pixel_step) do |i|
|
@@ -58,7 +58,7 @@ class PSD
|
|
58
58
|
name: fonts.first,
|
59
59
|
sizes: sizes,
|
60
60
|
colors: colors,
|
61
|
-
css:
|
61
|
+
css: to_css
|
62
62
|
}
|
63
63
|
end
|
64
64
|
|
@@ -80,7 +80,8 @@ class PSD
|
|
80
80
|
#
|
81
81
|
# => [[255, 0, 0, 255], [0, 0, 255, 255]]
|
82
82
|
def colors
|
83
|
-
|
83
|
+
# If the color is opaque black, this field is sometimes omitted.
|
84
|
+
return [[0, 0, 0, 255]] if engine_data.nil? || !styles.has_key?('FillColor')
|
84
85
|
styles['FillColor'].map { |s|
|
85
86
|
values = s['Values'].map { |v| (v * 255).round }
|
86
87
|
values << values.shift # Change ARGB -> RGBA for consistency
|
@@ -115,6 +116,26 @@ class PSD
|
|
115
116
|
@parser ||= PSD::EngineData.new(@data[:text]['EngineData'])
|
116
117
|
end
|
117
118
|
|
119
|
+
# Creates the CSS string and returns it. Each property is newline separated
|
120
|
+
# and not all properties may be present depending on the document.
|
121
|
+
#
|
122
|
+
# Colors are returned in rgba() format and fonts may include some internal
|
123
|
+
# Photoshop fonts.
|
124
|
+
def to_css
|
125
|
+
definition = {
|
126
|
+
'font-family' => fonts.join(', '),
|
127
|
+
'font-size' => "#{sizes.first}pt",
|
128
|
+
'color' => "rgba(#{colors.first.join(', ')})"
|
129
|
+
}
|
130
|
+
|
131
|
+
css = []
|
132
|
+
definition.each do |k, v|
|
133
|
+
css << "#{k}: #{v};"
|
134
|
+
end
|
135
|
+
|
136
|
+
css.join("\n")
|
137
|
+
end
|
138
|
+
|
118
139
|
def to_hash
|
119
140
|
{
|
120
141
|
value: text_value,
|
data/lib/psd/layer_mask.rb
CHANGED
@@ -115,7 +115,7 @@ class PSD
|
|
115
115
|
@global_mask = {}
|
116
116
|
@global_mask[:overlay_color_space] = @file.read_short
|
117
117
|
@global_mask[:color_components] = 4.times.map { |i| @file.read_short >> 8 }
|
118
|
-
@global_mask[:opacity] = @file.read_short
|
118
|
+
@global_mask[:opacity] = @file.read_short / 16.0
|
119
119
|
|
120
120
|
# 0 = color selected, 1 = color protected, 128 = use value per layer
|
121
121
|
@global_mask[:kind] = @file.read(1).bytes.to_a[0]
|
data/lib/psd/nodes/ancestry.rb
CHANGED
@@ -27,7 +27,7 @@ class PSD
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Inverse of has_children?
|
30
|
-
def
|
30
|
+
def childless?
|
31
31
|
!has_children?
|
32
32
|
end
|
33
33
|
|
@@ -44,7 +44,7 @@ class PSD
|
|
44
44
|
end
|
45
45
|
|
46
46
|
# Is this node the only descendant of its parent?
|
47
|
-
def
|
47
|
+
def only_child?
|
48
48
|
siblings.length == 1
|
49
49
|
end
|
50
50
|
|
data/lib/psd/version.rb
CHANGED
data/psd.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
|
|
20
20
|
|
21
21
|
gem.add_dependency "bindata"
|
22
22
|
gem.add_dependency "oily_png"
|
23
|
-
gem.add_dependency 'psd-enginedata'
|
23
|
+
gem.add_dependency 'psd-enginedata', '~> 1.0'
|
24
24
|
|
25
25
|
gem.test_files = Dir.glob("spec/**/*")
|
26
26
|
gem.add_development_dependency 'rspec'
|
data/spec/files/text.psd
CHANGED
Binary file
|
data/spec/hierarchy_spec.rb
CHANGED
@@ -9,8 +9,8 @@ describe "Hierarchy" do
|
|
9
9
|
@psd.parse!
|
10
10
|
|
11
11
|
tree = @psd.tree.to_hash
|
12
|
-
tree.
|
13
|
-
tree[:children].length.
|
12
|
+
expect(tree).to include :children
|
13
|
+
expect(tree[:children].length).to eq(3)
|
14
14
|
end
|
15
15
|
|
16
16
|
describe "Ancestry" do
|
@@ -20,66 +20,66 @@ describe "Hierarchy" do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should provide tree traversal methods" do
|
23
|
-
@tree.respond_to
|
24
|
-
@tree.respond_to
|
25
|
-
@tree.respond_to
|
26
|
-
@tree.respond_to
|
23
|
+
expect(@tree).to respond_to(:root)
|
24
|
+
expect(@tree).to respond_to(:siblings)
|
25
|
+
expect(@tree).to respond_to(:descendants)
|
26
|
+
expect(@tree).to respond_to(:subtree)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should properly identify the root node" do
|
30
|
-
@tree.root
|
31
|
-
@tree.root.
|
32
|
-
@tree.children.last.root.
|
30
|
+
expect(@tree.root?).to be_true
|
31
|
+
expect(@tree.root).to be @tree
|
32
|
+
expect(@tree.children.last.root).to be @tree
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should retrieve all descendants of a node" do
|
36
|
-
@tree.descendants.size.
|
37
|
-
@tree.descendant_layers.size.
|
38
|
-
@tree.descendant_groups.size.
|
39
|
-
@tree.descendants.first.
|
36
|
+
expect(@tree.descendants.size).to eq(12)
|
37
|
+
expect(@tree.descendant_layers.size).to eq(9)
|
38
|
+
expect(@tree.descendant_groups.size).to eq(3)
|
39
|
+
expect(@tree.descendants.first).not_to be @tree
|
40
40
|
end
|
41
41
|
|
42
42
|
it "should retreive the entire subtree of a node" do
|
43
|
-
@tree.subtree.size.
|
44
|
-
@tree.subtree_layers.size.
|
45
|
-
@tree.subtree_groups.size.
|
46
|
-
@tree.subtree.first.
|
43
|
+
expect(@tree.subtree.size).to eq(13)
|
44
|
+
expect(@tree.subtree_layers.size).to eq(9)
|
45
|
+
expect(@tree.subtree_groups.size).to eq(3)
|
46
|
+
expect(@tree.subtree.first).to be @tree
|
47
47
|
end
|
48
48
|
|
49
49
|
it "should properly identify the existence of children" do
|
50
|
-
@tree.
|
51
|
-
@tree.
|
52
|
-
@tree.descendant_layers.first.
|
53
|
-
@tree.descendant_layers.first.
|
50
|
+
expect(@tree).to have_children
|
51
|
+
expect(@tree).to_not be_childless
|
52
|
+
expect(@tree.descendant_layers.first).to_not have_children
|
53
|
+
expect(@tree.descendant_layers.first).to be_childless
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should retrieve all siblings of a node" do
|
57
|
-
@tree.children.first.siblings.
|
58
|
-
@tree.children.first.siblings.
|
59
|
-
@tree.children.first.
|
60
|
-
@tree.children.first.
|
57
|
+
expect(@tree.children.first.siblings).to be @tree.children
|
58
|
+
expect(@tree.children.first.siblings).to include @tree.children.first
|
59
|
+
expect(@tree.children.first).to have_siblings
|
60
|
+
expect(@tree.children.first).to_not be_only_child
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should properly calculate node depth" do
|
64
|
-
@tree.depth.
|
65
|
-
@tree.descendant_layers.last.depth.
|
66
|
-
@tree.children.first.depth.
|
64
|
+
expect(@tree.depth).to eq(0)
|
65
|
+
expect(@tree.descendant_layers.last.depth).to eq(2)
|
66
|
+
expect(@tree.children.first.depth).to eq(1)
|
67
67
|
end
|
68
68
|
|
69
69
|
describe "Searching" do
|
70
70
|
it "should find a node given a path" do
|
71
|
-
@tree.children_at_path('Version A/Matte').
|
72
|
-
@tree.children_at_path('Version A/Matte').size.
|
73
|
-
@tree.children_at_path('Version A/Matte').first.
|
71
|
+
expect(@tree.children_at_path('Version A/Matte')).to be_an_instance_of(Array)
|
72
|
+
expect(@tree.children_at_path('Version A/Matte').size).to eq(1)
|
73
|
+
expect(@tree.children_at_path('Version A/Matte').first).to be_an_instance_of(PSD::Node::Layer)
|
74
74
|
end
|
75
75
|
|
76
76
|
it "should ignore leading slashes" do
|
77
|
-
@tree.children_at_path('/Version A/Matte').size.
|
77
|
+
expect(@tree.children_at_path('/Version A/Matte').size).to eq(1)
|
78
78
|
end
|
79
79
|
|
80
80
|
it "should return an empty array when a node is not found" do
|
81
|
-
@tree.children_at_path('
|
82
|
-
@tree.children_at_path('
|
81
|
+
expect(@tree.children_at_path('NOPE')).to be_an_instance_of(Array)
|
82
|
+
expect(@tree.children_at_path('NOPE').size).to eq(0)
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
data/spec/identity_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe "Identity exports" do
|
|
9
9
|
tmpfile = Tempfile.new("simplest_export.psd")
|
10
10
|
psd.export tmpfile.path
|
11
11
|
|
12
|
-
Digest::MD5.hexdigest(tmpfile.read).
|
12
|
+
expect(Digest::MD5.hexdigest(tmpfile.read)).to eq(Digest::MD5.hexdigest(File.read(filepath)))
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should export a file with a layer" do
|
@@ -19,7 +19,7 @@ describe "Identity exports" do
|
|
19
19
|
tmpfile = Tempfile.new("one_layer_export.psd")
|
20
20
|
psd.export tmpfile.path
|
21
21
|
|
22
|
-
Digest::MD5.hexdigest(tmpfile.read).
|
22
|
+
expect(Digest::MD5.hexdigest(tmpfile.read)).to eq(Digest::MD5.hexdigest(File.read(filepath)))
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should export a PSD with vector paths" do
|
@@ -29,6 +29,6 @@ describe "Identity exports" do
|
|
29
29
|
tmpfile = Tempfile.new("path_export.psd")
|
30
30
|
psd.export tmpfile.path
|
31
31
|
|
32
|
-
Digest::MD5.hexdigest(tmpfile.read).
|
32
|
+
expect(Digest::MD5.hexdigest(tmpfile.read)).to eq(Digest::MD5.hexdigest(File.read(filepath)))
|
33
33
|
end
|
34
34
|
end
|
data/spec/parsing_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe 'Parsing' do
|
|
7
7
|
|
8
8
|
it "should parse without error" do
|
9
9
|
@psd.parse!
|
10
|
-
@psd.
|
10
|
+
expect(@psd).to be_parsed
|
11
11
|
end
|
12
12
|
|
13
13
|
describe 'Header' do
|
@@ -16,25 +16,25 @@ describe 'Parsing' do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should contain data" do
|
19
|
-
@psd.header.
|
19
|
+
expect(@psd.header).not_to be_nil
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should be the proper version" do
|
23
|
-
@psd.header.version.
|
23
|
+
expect(@psd.header.version).to eq(1)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should have the proper number of channels" do
|
27
|
-
@psd.header.channels.
|
27
|
+
expect(@psd.header.channels).to eq(3)
|
28
28
|
end
|
29
29
|
|
30
30
|
it "should parse the proper document dimensions" do
|
31
|
-
@psd.header.width.
|
32
|
-
@psd.header.height.
|
31
|
+
expect(@psd.header.width).to eq(900)
|
32
|
+
expect(@psd.header.height).to eq(600)
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should correctly parse the color mode" do
|
36
|
-
@psd.header.mode.
|
37
|
-
@psd.header.mode_name.
|
36
|
+
expect(@psd.header.mode).to eq(3)
|
37
|
+
expect(@psd.header.mode_name).to eq('RGBColor')
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -44,18 +44,18 @@ describe 'Parsing' do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
it "should contain data" do
|
47
|
-
@psd.resources.
|
48
|
-
@psd.resources.data.
|
49
|
-
@psd.resources.data.size.
|
47
|
+
expect(@psd.resources).not_to be_nil
|
48
|
+
expect(@psd.resources.data).to be_an_instance_of(Hash)
|
49
|
+
expect(@psd.resources.data.size).to be >= 1
|
50
50
|
end
|
51
51
|
|
52
52
|
it "should be of type 8BIM" do
|
53
|
-
@psd.resources.data.each { |id, r| r.type.
|
53
|
+
@psd.resources.data.each { |id, r| expect(r.type).to eq('8BIM') }
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should have an ID" do
|
57
57
|
@psd.resources.data.each do |id, r|
|
58
|
-
r.id.
|
58
|
+
expect(r.id).to_not be_nil
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -66,18 +66,19 @@ describe 'Parsing' do
|
|
66
66
|
end
|
67
67
|
|
68
68
|
it "should contain data" do
|
69
|
-
@psd.layer_mask.
|
70
|
-
@psd.layer_mask.
|
69
|
+
expect(@psd.layer_mask).to_not be_nil
|
70
|
+
expect(@psd.layer_mask).to be_an_instance_of(PSD::LayerMask)
|
71
71
|
end
|
72
72
|
|
73
73
|
it "should contain layers" do
|
74
|
-
@psd.layer_mask.layers.size.
|
74
|
+
expect(@psd.layer_mask.layers.size).to be > 0
|
75
75
|
end
|
76
76
|
|
77
77
|
it "should contain the global layer mask data" do
|
78
|
-
|
79
|
-
|
80
|
-
@psd.layer_mask.global_mask.
|
78
|
+
expect(@psd.layer_mask.global_mask).to_not be_nil
|
79
|
+
expect(@psd.layer_mask.global_mask).to include :overlay_color_space
|
80
|
+
expect(@psd.layer_mask.global_mask).to include :color_components
|
81
|
+
expect(@psd.layer_mask.global_mask).to include opacity: 1.0
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
@@ -87,48 +88,48 @@ describe 'Parsing' do
|
|
87
88
|
end
|
88
89
|
|
89
90
|
it "should contain each layer" do
|
90
|
-
@psd.layer_mask.layers.size.
|
91
|
-
@psd.layers.
|
92
|
-
@psd.layers.each { |l| l.
|
91
|
+
expect(@psd.layer_mask.layers.size).to eq(15)
|
92
|
+
expect(@psd.layers).to be @psd.layer_mask.layers
|
93
|
+
@psd.layers.each { |l| expect(l).to be_an_instance_of(PSD::Layer) }
|
93
94
|
end
|
94
95
|
|
95
96
|
it "should have a name" do
|
96
|
-
@psd.layers.first.name.
|
97
|
+
expect(@psd.layers.first.name).to eq('Version C')
|
97
98
|
end
|
98
99
|
|
99
100
|
it "should properly identify folders" do
|
100
|
-
@psd.layers.first.
|
101
|
-
@psd.layers.select { |l| l.name == 'Matte' }.first.
|
101
|
+
expect(@psd.layers.first).to be_folder
|
102
|
+
expect(@psd.layers.select { |l| l.name == 'Matte' }.first).not_to be_folder
|
102
103
|
end
|
103
104
|
|
104
105
|
it "should properly detect visibility" do
|
105
|
-
@psd.layers.first.
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
106
|
+
expect(@psd.layers.first).not_to be_visible
|
107
|
+
expect(
|
108
|
+
@psd
|
109
|
+
.layers
|
110
|
+
.select { |l| l.name == 'Version A' }.first
|
111
|
+
).to be_visible
|
111
112
|
end
|
112
113
|
|
113
114
|
it "should properly calculate dimensions" do
|
114
115
|
layer = @psd.layers.select { |l| l.name == 'Logo_Glyph' }.last
|
115
|
-
layer.width.
|
116
|
-
layer.height.
|
116
|
+
expect(layer.width).to eq(142)
|
117
|
+
expect(layer.height).to eq(179)
|
117
118
|
end
|
118
119
|
|
119
120
|
it "should properly calculate coordinates" do
|
120
121
|
layer = @psd.layers.select { |l| l.name == 'Logo_Glyph' }.last
|
121
|
-
layer.left.
|
122
|
-
layer.top.
|
122
|
+
expect(layer.left).to eq(379)
|
123
|
+
expect(layer.top).to eq(210)
|
123
124
|
end
|
124
125
|
|
125
126
|
it "should have a blend mode" do
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
127
|
+
blend_mode = @psd.layers.select { |l| l.name == 'Version A' }.last.blend_mode
|
128
|
+
expect(blend_mode).to_not be_nil
|
129
|
+
expect(blend_mode.mode).to eq('normal')
|
130
|
+
expect(blend_mode.opacity).to eq(255)
|
131
|
+
expect(blend_mode.opacity_percentage).to eq(100)
|
132
|
+
expect(blend_mode.visible).to be_true
|
132
133
|
end
|
133
134
|
end
|
134
135
|
end
|
data/spec/psd_spec.rb
CHANGED
@@ -5,8 +5,8 @@ describe 'PSD' do
|
|
5
5
|
|
6
6
|
it 'should open a file without a block' do
|
7
7
|
psd = PSD.open(filename)
|
8
|
-
psd.
|
9
|
-
psd.
|
8
|
+
expect(psd).to be_parsed
|
9
|
+
expect(psd).to be_an_instance_of(PSD)
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'should refuse to open a bad filename' do
|
@@ -15,11 +15,13 @@ describe 'PSD' do
|
|
15
15
|
|
16
16
|
it 'should open a file and feed it to a block' do
|
17
17
|
PSD.open(filename) do |psd|
|
18
|
-
psd.
|
19
|
-
psd.
|
18
|
+
expect(psd).to be_parsed
|
19
|
+
expect(psd).to be_an_instance_of(PSD)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
# We have to use #should syntax here because the DSL binds
|
24
|
+
# the block to the PSD instance.
|
23
25
|
it 'should open a file and feed it to a block DSL style' do
|
24
26
|
PSD.open(filename) do
|
25
27
|
parsed?.should == true
|
data/spec/text_spec.rb
CHANGED
@@ -7,6 +7,21 @@ describe 'Text' do
|
|
7
7
|
|
8
8
|
text = psd.tree.children.first.text
|
9
9
|
text.should be_an_instance_of(Hash)
|
10
|
-
text[:value].
|
10
|
+
expect(text[:value]).to eq('Test')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can be exported as CSS" do
|
14
|
+
psd = PSD.new('spec/files/text.psd')
|
15
|
+
psd.parse!
|
16
|
+
|
17
|
+
type = psd.tree.children.first.type
|
18
|
+
css = type.to_css
|
19
|
+
expect(css).to be_an_instance_of(String)
|
20
|
+
expect(css).to include 'MyriadPro-Regular'
|
21
|
+
expect(css).to include '37.0pt'
|
22
|
+
expect(css).to include 'rgba(24, 24, 24, 255)'
|
23
|
+
css.split(/\n/).each do |c|
|
24
|
+
expect(c[-1]).to eq(";")
|
25
|
+
end
|
11
26
|
end
|
12
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: psd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan LeFevre
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-08-
|
12
|
+
date: 2013-08-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bindata
|
@@ -43,16 +43,16 @@ dependencies:
|
|
43
43
|
name: psd-enginedata
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ~>
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
48
|
+
version: '1.0'
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
55
|
+
version: '1.0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: rspec
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|