psd 1.4.5 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/psd/channel_image.rb +0 -1
- data/lib/psd/compose.rb +1 -1
- data/lib/psd/file.rb +1 -1
- data/lib/psd/image_exports/png.rb +20 -17
- data/lib/psd/image_formats/layer_rle.rb +1 -1
- data/lib/psd/image_formats/rle.rb +1 -1
- data/lib/psd/layer/helpers.rb +24 -0
- data/lib/psd/layer/info.rb +2 -0
- data/lib/psd/layer_info/locked.rb +19 -0
- data/lib/psd/layer_info/typetool.rb +1 -1
- data/lib/psd/layer_mask.rb +0 -1
- data/lib/psd/node_group.rb +9 -4
- data/lib/psd/node_layer.rb +4 -0
- data/lib/psd/nodes/build_preview.rb +9 -9
- data/lib/psd/resources/slices.rb +118 -13
- data/lib/psd/version.rb +1 -1
- data/spec/files/empty-layer.psd +0 -0
- data/spec/files/locked.psd +0 -0
- data/spec/hierarchy_spec.rb +21 -1
- data/spec/image_spec.rb +44 -28
- data/spec/locked_spec.rb +78 -0
- data/spec/parsing_spec.rb +1 -1
- data/spec/slices_spec.rb +15 -10
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87dd8d12bd49feb7ad2502047fa2ec73d4aff7b1
|
4
|
+
data.tar.gz: 5647eca77e19f0a4ebb3ac59448d8b26d0f56da8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d82f2fde33a13a60863548bc75788ffd279651e1c66a553749d794080733463e4f78cf13cfbcbf58a4d6296579f678cc3e32a0e6419c83ef7273e1b41d1406c5
|
7
|
+
data.tar.gz: 23a3d1ba099ed12c363958af463393e56702e2ac2a02ba67a9af247016fbe2f384f893f5ea5dad4bdb651cb76196f9a77d42efe69cf7ced445ea6177e4ffbffe
|
data/lib/psd/channel_image.rb
CHANGED
data/lib/psd/compose.rb
CHANGED
@@ -17,7 +17,7 @@ class PSD
|
|
17
17
|
|
18
18
|
# Normal composition, delegate to ChunkyPNG
|
19
19
|
def normal(fg, bg, opts={})
|
20
|
-
return fg if
|
20
|
+
return fg if fully_transparent?(bg)
|
21
21
|
return bg if fully_transparent?(fg)
|
22
22
|
|
23
23
|
mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
|
data/lib/psd/file.rb
CHANGED
@@ -72,7 +72,7 @@ class PSD
|
|
72
72
|
# Reads a string of the given length and converts it to UTF-8 from the internally used MacRoman encoding.
|
73
73
|
def read_string(length=nil)
|
74
74
|
length = @file.read(1).bytes.to_a[0] if length.nil?
|
75
|
-
read(length).
|
75
|
+
read(length).delete("\000")
|
76
76
|
end
|
77
77
|
|
78
78
|
def read_byte
|
@@ -7,31 +7,34 @@ class PSD::Image
|
|
7
7
|
# Load the image pixels into a PNG file and return a reference to the
|
8
8
|
# data.
|
9
9
|
def to_png
|
10
|
+
return @png if @png
|
11
|
+
|
10
12
|
PSD.logger.debug "Beginning PNG export"
|
11
|
-
png = ChunkyPNG::Canvas.new(width.to_i, height.to_i, ChunkyPNG::Color::TRANSPARENT)
|
13
|
+
@png = ChunkyPNG::Canvas.new(width.to_i, height.to_i, ChunkyPNG::Color::TRANSPARENT)
|
12
14
|
|
13
15
|
i = 0
|
14
16
|
height.times do |y|
|
15
17
|
width.times do |x|
|
16
|
-
png[x,y] = @pixel_data[i]
|
18
|
+
@png[x,y] = @pixel_data[i]
|
17
19
|
i += 1
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
png
|
23
|
+
@png
|
22
24
|
end
|
23
25
|
alias :export :to_png
|
24
26
|
|
25
27
|
def to_png_with_mask
|
26
28
|
return to_png unless has_mask?
|
29
|
+
return @png_with_mask if @png_with_mask
|
27
30
|
|
28
31
|
PSD.logger.debug "Beginning PNG export with mask"
|
29
|
-
|
32
|
+
|
30
33
|
# We generate the preview at the document size instead to make applying the mask
|
31
34
|
# significantly easier.
|
32
35
|
width = @layer.header.width.to_i
|
33
36
|
height = @layer.header.height.to_i
|
34
|
-
|
37
|
+
@png_with_mask = ChunkyPNG::Canvas.new(width, height, ChunkyPNG::Color::TRANSPARENT)
|
35
38
|
|
36
39
|
i = 0
|
37
40
|
@layer.height.times do |y|
|
@@ -39,13 +42,13 @@ class PSD::Image
|
|
39
42
|
offset_x = x + @layer.left
|
40
43
|
offset_y = y + @layer.top
|
41
44
|
|
42
|
-
i +=1 and next if offset_x < 0 || offset_y < 0 || offset_x >=
|
45
|
+
i +=1 and next if offset_x < 0 || offset_y < 0 || offset_x >= @png_with_mask.width || offset_y >= @png_with_mask.height
|
43
46
|
|
44
|
-
|
47
|
+
@png_with_mask[offset_x, offset_y] = @pixel_data[i]
|
45
48
|
i += 1
|
46
49
|
end
|
47
50
|
end
|
48
|
-
|
51
|
+
|
49
52
|
# Now we apply the mask
|
50
53
|
i = 0
|
51
54
|
@layer.mask.height.times do |y|
|
@@ -53,22 +56,22 @@ class PSD::Image
|
|
53
56
|
offset_x = @layer.mask.left + x
|
54
57
|
offset_y = @layer.mask.top + y
|
55
58
|
|
56
|
-
i += 1 and next if offset_x < 0 || offset_y < 0 || offset_x >=
|
59
|
+
i += 1 and next if offset_x < 0 || offset_y < 0 || offset_x >= @png_with_mask.width || offset_y >= @png_with_mask.height
|
57
60
|
|
58
|
-
color = ChunkyPNG::Color.to_truecolor_alpha_bytes(
|
61
|
+
color = ChunkyPNG::Color.to_truecolor_alpha_bytes(@png_with_mask[offset_x, offset_y])
|
59
62
|
color[3] = color[3] * @mask_data[i] / 255
|
60
63
|
|
61
|
-
|
64
|
+
@png_with_mask[offset_x, offset_y] = ChunkyPNG::Color.rgba(*color)
|
62
65
|
i += 1
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
66
|
-
crop_left = PSD::Util.clamp(@layer.left, 0,
|
67
|
-
crop_top = PSD::Util.clamp(@layer.top, 0,
|
68
|
-
crop_width = PSD::Util.clamp(@layer.width.to_i, 0,
|
69
|
-
crop_height = PSD::Util.clamp(@layer.height.to_i, 0,
|
69
|
+
crop_left = PSD::Util.clamp(@layer.left, 0, @png_with_mask.width)
|
70
|
+
crop_top = PSD::Util.clamp(@layer.top, 0, @png_with_mask.height)
|
71
|
+
crop_width = PSD::Util.clamp(@layer.width.to_i, 0, @png_with_mask.width - crop_left)
|
72
|
+
crop_height = PSD::Util.clamp(@layer.height.to_i, 0, @png_with_mask.height - crop_top)
|
70
73
|
|
71
|
-
|
74
|
+
@png_with_mask.crop!(crop_left, crop_top, crop_width, crop_height)
|
72
75
|
end
|
73
76
|
|
74
77
|
def mask_to_png
|
@@ -93,4 +96,4 @@ class PSD::Image
|
|
93
96
|
end
|
94
97
|
end
|
95
98
|
end
|
96
|
-
end
|
99
|
+
end
|
@@ -16,7 +16,7 @@ class PSD
|
|
16
16
|
def parse_channel_data!
|
17
17
|
@line_index = 0
|
18
18
|
|
19
|
-
PSD.logger.debug "Parsing
|
19
|
+
PSD.logger.debug "Parsing RLE channel ##{@ch_info[:id]}: file position = #{@file.tell}, image position = #{@chan_pos}, line = #{@line_index}"
|
20
20
|
decode_rle_channel
|
21
21
|
end
|
22
22
|
end
|
@@ -23,7 +23,7 @@ class PSD
|
|
23
23
|
@line_index = 0
|
24
24
|
|
25
25
|
channels.times do |i|
|
26
|
-
PSD.logger.debug "Parsing RLE channel ##{i}: position = #{@chan_pos}, line = #{@line_index}"
|
26
|
+
PSD.logger.debug "Parsing RLE channel ##{i}: file position = #{@file.tell}, image position = #{@chan_pos}, line = #{@line_index}"
|
27
27
|
decode_rle_channel
|
28
28
|
@line_index += height
|
29
29
|
end
|
data/lib/psd/layer/helpers.rb
CHANGED
@@ -23,6 +23,30 @@ class PSD
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
# Is the layer completely locked?
|
27
|
+
def all_locked?
|
28
|
+
return false unless info.has_key?(:locked)
|
29
|
+
info[:locked].all_locked
|
30
|
+
end
|
31
|
+
|
32
|
+
# Is the layer position locked?
|
33
|
+
def position_locked?
|
34
|
+
return false unless info.has_key?(:locked)
|
35
|
+
info[:locked].position_locked
|
36
|
+
end
|
37
|
+
|
38
|
+
# Is the layer composite locked?
|
39
|
+
def composite_locked?
|
40
|
+
return false unless info.has_key?(:locked)
|
41
|
+
info[:locked].composite_locked
|
42
|
+
end
|
43
|
+
|
44
|
+
# Is the layer transparency locked?
|
45
|
+
def transparency_locked?
|
46
|
+
return false unless info.has_key?(:locked)
|
47
|
+
info[:locked].transparency_locked
|
48
|
+
end
|
49
|
+
|
26
50
|
# Is this layer visible?
|
27
51
|
def visible?
|
28
52
|
@visible
|
data/lib/psd/layer/info.rb
CHANGED
@@ -17,6 +17,7 @@ class PSD
|
|
17
17
|
layer_id: LayerID,
|
18
18
|
fill_opacity: FillOpacity,
|
19
19
|
placed_layer: PlacedLayer,
|
20
|
+
locked: Locked,
|
20
21
|
vector_mask: VectorMask,
|
21
22
|
vector_mask_2: VectorMask2,
|
22
23
|
vector_stroke: VectorStroke,
|
@@ -52,6 +53,7 @@ class PSD
|
|
52
53
|
|
53
54
|
i = info.new(self, length)
|
54
55
|
@adjustments[name] = LazyExecute.new(i, @file).now(:skip).later(:parse)
|
56
|
+
|
55
57
|
key_parseable = true and break
|
56
58
|
end
|
57
59
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Locked < LayerInfo
|
5
|
+
@key = 'lspf'
|
6
|
+
|
7
|
+
attr_reader :all_locked, :transparency_locked, :composite_locked, :position_locked
|
8
|
+
|
9
|
+
def parse
|
10
|
+
locked = @file.read_int
|
11
|
+
|
12
|
+
@transparency_locked = (locked & (0x01 << 0)) > 0 || locked == -2147483648
|
13
|
+
@composite_locked = (locked & (0x01 << 1)) > 0 || locked == -2147483648
|
14
|
+
@position_locked = (locked & (0x01 << 2)) > 0 || locked == -2147483648
|
15
|
+
|
16
|
+
@all_locked = @transparency_locked && @composite_locked && @position_locked
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/psd/layer_mask.rb
CHANGED
data/lib/psd/node_group.rb
CHANGED
@@ -46,6 +46,10 @@ class PSD::Node
|
|
46
46
|
@children.each{ |c| c.show! }
|
47
47
|
end
|
48
48
|
|
49
|
+
def empty?
|
50
|
+
@children.empty?
|
51
|
+
end
|
52
|
+
|
49
53
|
# Export this layer and it's children to a hash recursively.
|
50
54
|
def to_hash
|
51
55
|
super.merge({
|
@@ -64,10 +68,11 @@ class PSD::Node
|
|
64
68
|
private
|
65
69
|
|
66
70
|
def get_dimensions
|
67
|
-
|
68
|
-
@
|
69
|
-
@
|
70
|
-
@
|
71
|
+
children = @children.reject(&:empty?)
|
72
|
+
@left = children.map(&:left).min || 0
|
73
|
+
@top = children.map(&:top).min || 0
|
74
|
+
@bottom = children.map(&:bottom).max || 0
|
75
|
+
@right = children.map(&:right).max || 0
|
71
76
|
end
|
72
77
|
end
|
73
78
|
end
|
data/lib/psd/node_layer.rb
CHANGED
@@ -6,7 +6,7 @@ class PSD
|
|
6
6
|
alias :orig_to_png :to_png
|
7
7
|
def to_png
|
8
8
|
return build_png if group?
|
9
|
-
layer.image.
|
9
|
+
layer.image.to_png_with_mask
|
10
10
|
end
|
11
11
|
|
12
12
|
def build_png(png=nil)
|
@@ -14,7 +14,7 @@ class PSD
|
|
14
14
|
|
15
15
|
children.reverse.each do |c|
|
16
16
|
next unless c.visible?
|
17
|
-
|
17
|
+
|
18
18
|
if c.group?
|
19
19
|
if c.blending_mode == 'passthru'
|
20
20
|
c.build_png(png)
|
@@ -23,10 +23,10 @@ class PSD
|
|
23
23
|
end
|
24
24
|
else
|
25
25
|
compose!(
|
26
|
-
c,
|
27
|
-
png,
|
28
|
-
c.image.to_png_with_mask,
|
29
|
-
PSD::Util.clamp(c.left.to_i, 0, png.width),
|
26
|
+
c,
|
27
|
+
png,
|
28
|
+
c.image.to_png_with_mask,
|
29
|
+
PSD::Util.clamp(c.left.to_i, 0, png.width),
|
30
30
|
PSD::Util.clamp(c.top.to_i, 0, png.height)
|
31
31
|
)
|
32
32
|
end
|
@@ -59,11 +59,11 @@ class PSD
|
|
59
59
|
other.width.times do |x|
|
60
60
|
base_x = x + offset_x
|
61
61
|
base_y = y + offset_y
|
62
|
-
|
62
|
+
|
63
63
|
next if base_x < 0 || base_y < 0 || base_x >= base.width || base_y >= base.height
|
64
64
|
|
65
65
|
color = Compose.send(
|
66
|
-
blending_mode,
|
66
|
+
blending_mode,
|
67
67
|
other[x, y],
|
68
68
|
base[base_x, base_y],
|
69
69
|
opacity: layer.opacity,
|
@@ -76,4 +76,4 @@ class PSD
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
79
|
-
end
|
79
|
+
end
|
data/lib/psd/resources/slices.rb
CHANGED
@@ -1,27 +1,132 @@
|
|
1
1
|
class PSD
|
2
2
|
class Resource
|
3
3
|
class Section
|
4
|
-
|
5
|
-
|
4
|
+
class Slices < Section
|
5
|
+
attr_reader :data, :version
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
def self.id; 1050; end
|
8
|
+
def self.name; :slices; end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
@descriptor_version = @file.read_int
|
13
|
-
@resource.data = self
|
10
|
+
def parse
|
11
|
+
@version = @file.read_int
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
case @version
|
14
|
+
when 6 then parse_legacy
|
15
|
+
when 7, 8 then
|
16
|
+
descriptor_version = @file.read_int
|
17
|
+
@data = Descriptor.new(@file).parse
|
18
|
+
end
|
19
|
+
|
20
|
+
normalize_data!
|
21
|
+
|
22
|
+
@resource.data = self
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_a
|
26
|
+
return [] if @data.nil?
|
27
|
+
@data[:slices]
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def parse_legacy
|
33
|
+
@data = {}
|
34
|
+
|
35
|
+
@data[:bounds] = {}.tap do |bounds|
|
36
|
+
bounds[:top] = @file.read_int
|
37
|
+
bounds[:left] = @file.read_int
|
38
|
+
bounds[:bottom] = @file.read_int
|
39
|
+
bounds[:right] = @file.read_int
|
40
|
+
end
|
41
|
+
|
42
|
+
@data[:name] = @file.read_unicode_string
|
18
43
|
|
44
|
+
@data[:slices] = []
|
45
|
+
slices_count = @file.read_int
|
46
|
+
slices_count.times do
|
47
|
+
@data[:slices] << {}.tap do |slice|
|
48
|
+
slice[:id] = @file.read_int
|
49
|
+
slice[:group_id] = @file.read_int
|
50
|
+
slice[:origin] = @file.read_int
|
51
|
+
|
52
|
+
slice[:associated_layer_id] = (slice[:origin] == 1 ? @file.read_int : nil)
|
53
|
+
|
54
|
+
slice[:name] = @file.read_unicode_string
|
55
|
+
slice[:type] = @file.read_int
|
56
|
+
|
57
|
+
slice[:bounds] = {}.tap do |bounds|
|
58
|
+
bounds[:left] = @file.read_int
|
59
|
+
bounds[:top] = @file.read_int
|
60
|
+
bounds[:right] = @file.read_int
|
61
|
+
bounds[:bottom] = @file.read_int
|
62
|
+
end
|
63
|
+
|
64
|
+
slice[:url] = @file.read_unicode_string
|
65
|
+
slice[:target] = @file.read_unicode_string
|
66
|
+
slice[:message] = @file.read_unicode_string
|
67
|
+
slice[:alt] = @file.read_unicode_string
|
68
|
+
|
69
|
+
slice[:cell_text_is_html] = @file.read_boolean
|
70
|
+
slice[:cell_text] = @file.read_unicode_string
|
71
|
+
|
72
|
+
slice[:horizontal_alignment] = @file.read_int
|
73
|
+
slice[:vertical_alignment] = @file.read_int
|
74
|
+
|
75
|
+
a, r, g, b = 4.times.map { @file.read_byte }
|
76
|
+
slice[:color] = ChunkyPNG::Color.rgba(r, g, b, a)
|
77
|
+
end
|
19
78
|
end
|
79
|
+
end
|
20
80
|
|
21
|
-
|
22
|
-
|
81
|
+
# Normalizes the data between version 6 and versions 7/8 to make it easier
|
82
|
+
# to deal with the discrepancies. We use version 6 as the base.
|
83
|
+
def normalize_data!
|
84
|
+
return if @version == 6
|
85
|
+
data = {}
|
86
|
+
data[:bounds] = {
|
87
|
+
top: @data['bounds']['Top '],
|
88
|
+
left: @data['bounds']['Left'],
|
89
|
+
bottom: @data['bounds']['Btom'],
|
90
|
+
right: @data['bounds']['Rght']
|
91
|
+
}
|
92
|
+
|
93
|
+
data[:name] = @data['baseName']
|
94
|
+
|
95
|
+
data[:slices] = @data['slices'].map do |slice|
|
96
|
+
{
|
97
|
+
id: slice['sliceID'],
|
98
|
+
group_id: slice['groupID'],
|
99
|
+
origin: slice['origin'],
|
100
|
+
associated_layer_id: nil,
|
101
|
+
name: '',
|
102
|
+
type: slice['Type'],
|
103
|
+
bounds: {
|
104
|
+
left: slice['bounds']['Left'],
|
105
|
+
top: slice['bounds']['Top '],
|
106
|
+
right: slice['bounds']['Rght'],
|
107
|
+
bottom: slice['bounds']['Btom']
|
108
|
+
},
|
109
|
+
url: slice['url'],
|
110
|
+
target: '',
|
111
|
+
message: slice['Msge'],
|
112
|
+
alt: slice['altTag'],
|
113
|
+
cell_text_is_html: slice['cellTextIsHTML'],
|
114
|
+
cell_text: slice['cellText'],
|
115
|
+
horizontal_alignment: slice['horzAlign'],
|
116
|
+
vertical_alignment: slice['vertAlign'],
|
117
|
+
color: nil,
|
118
|
+
outset: {
|
119
|
+
top: slice['topOutset'],
|
120
|
+
left: slice['leftOutset'],
|
121
|
+
bottom: slice['bottomOutset'],
|
122
|
+
right: slice['rightOutset']
|
123
|
+
}
|
124
|
+
}
|
23
125
|
end
|
126
|
+
|
127
|
+
@data = data
|
24
128
|
end
|
129
|
+
end
|
25
130
|
end
|
26
131
|
end
|
27
132
|
end
|
data/lib/psd/version.rb
CHANGED
Binary file
|
Binary file
|
data/spec/hierarchy_spec.rb
CHANGED
@@ -27,7 +27,7 @@ describe "Hierarchy" do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should properly identify the root node" do
|
30
|
-
expect(@tree.root?).to
|
30
|
+
expect(@tree.root?).to be true
|
31
31
|
expect(@tree.root).to be @tree
|
32
32
|
expect(@tree.children.last.root).to be @tree
|
33
33
|
end
|
@@ -105,4 +105,24 @@ describe "Hierarchy" do
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
end
|
108
|
+
|
109
|
+
# https://github.com/layervault/psd.rb/pull/54
|
110
|
+
describe "Size Calculation" do
|
111
|
+
before(:each) do
|
112
|
+
@psd = PSD.new('spec/files/empty-layer.psd')
|
113
|
+
@psd.parse!
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should correctly identify empty nodes' do
|
117
|
+
expect(@psd.tree.children_at_path('group/empty layer').first).to be_empty
|
118
|
+
expect(@psd.tree.children[0]).to_not be_empty
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should correctly calculate the size of a group" do
|
122
|
+
expect(@psd.tree.children[0].width).to eq 100
|
123
|
+
expect(@psd.tree.children[0].height).to eq 100
|
124
|
+
expect(@psd.tree.children[0].left).to eq 450
|
125
|
+
expect(@psd.tree.children[0].top).to eq 450
|
126
|
+
end
|
127
|
+
end
|
108
128
|
end
|
data/spec/image_spec.rb
CHANGED
@@ -1,39 +1,33 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Image Exporting' do
|
4
|
-
|
5
|
-
class PSD::Image
|
6
|
-
attr_accessor :pixel_data
|
7
|
-
end
|
8
|
-
|
9
|
-
@psd = PSD.new('spec/files/pixel.psd')
|
10
|
-
end
|
4
|
+
let(:psd) { PSD.new('spec/files/pixel.psd') }
|
11
5
|
|
12
6
|
describe "the full preview image" do
|
13
7
|
it "should successfully parse the image data" do
|
14
|
-
|
15
|
-
expect(
|
16
|
-
expect(
|
17
|
-
expect(
|
18
|
-
expect(
|
19
|
-
expect(
|
8
|
+
psd.parse!
|
9
|
+
expect(psd).to be_parsed
|
10
|
+
expect(psd.image).to_not be_nil
|
11
|
+
expect(psd.image.width).to eq(1)
|
12
|
+
expect(psd.image.height).to eq(1)
|
13
|
+
expect(psd.image.pixel_data).to eq([ChunkyPNG::Color.rgba(0, 100, 200, 255)])
|
20
14
|
end
|
21
15
|
|
22
16
|
it "should be able to skip to the image" do
|
23
|
-
expect(
|
24
|
-
expect(
|
25
|
-
expect(
|
26
|
-
expect(
|
17
|
+
expect(psd).to_not be_parsed
|
18
|
+
expect(psd.image.width).to eq(1)
|
19
|
+
expect(psd.image.height).to eq(1)
|
20
|
+
expect(psd.image.pixel_data).to eq([ChunkyPNG::Color.rgba(0, 100, 200, 255)])
|
27
21
|
end
|
28
22
|
|
29
23
|
describe "as PNG" do
|
30
24
|
it "should produce a valid PNG object" do
|
31
|
-
expect(
|
25
|
+
expect(psd.image.to_png).to be_an_instance_of(ChunkyPNG::Canvas)
|
32
26
|
|
33
|
-
expect(
|
34
|
-
expect(
|
27
|
+
expect(psd.image.to_png.width).to eq(1)
|
28
|
+
expect(psd.image.to_png.height).to eq(1)
|
35
29
|
expect(
|
36
|
-
ChunkyPNG::Color.to_truecolor_alpha_bytes(
|
30
|
+
ChunkyPNG::Color.to_truecolor_alpha_bytes(psd.image.to_png[0,0])
|
37
31
|
).to eq([0, 100, 200, 255])
|
38
32
|
end
|
39
33
|
end
|
@@ -41,10 +35,10 @@ describe 'Image Exporting' do
|
|
41
35
|
|
42
36
|
describe "layer images" do
|
43
37
|
it "should successfully parse the image data" do
|
44
|
-
|
45
|
-
|
38
|
+
psd.options[:parse_layer_images] = true
|
39
|
+
psd.parse!
|
46
40
|
|
47
|
-
image =
|
41
|
+
image = psd.tree.children.first.image
|
48
42
|
expect(image.width).to eq(1)
|
49
43
|
expect(image.height).to eq(1)
|
50
44
|
|
@@ -53,10 +47,10 @@ describe 'Image Exporting' do
|
|
53
47
|
|
54
48
|
describe "as PNG" do
|
55
49
|
it "should produce a valid PNG object" do
|
56
|
-
|
57
|
-
|
50
|
+
psd.options[:parse_layer_images] = true
|
51
|
+
psd.parse!
|
58
52
|
|
59
|
-
png =
|
53
|
+
png = psd.tree.children.first.image.to_png
|
60
54
|
expect(png).to be_an_instance_of(ChunkyPNG::Canvas)
|
61
55
|
expect(png.width).to eq(1)
|
62
56
|
expect(png.height).to eq(1)
|
@@ -64,6 +58,28 @@ describe 'Image Exporting' do
|
|
64
58
|
ChunkyPNG::Color.to_truecolor_alpha_bytes(png[0,0])
|
65
59
|
).to eq([0, 100, 200, 255])
|
66
60
|
end
|
61
|
+
|
62
|
+
it "memorizes the png instance" do
|
63
|
+
psd.options[:parse_layer_images] = true
|
64
|
+
psd.parse!
|
65
|
+
|
66
|
+
node = psd.tree.children.first
|
67
|
+
png = node.image.to_png
|
68
|
+
|
69
|
+
expect(png).to be_an_instance_of(ChunkyPNG::Canvas)
|
70
|
+
expect(node.image.to_png.__id__).to eq(png.__id__)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "memorizes the png_with_mask instance" do
|
74
|
+
psd = PSD.new('spec/files/path.psd', parse_layer_images: true)
|
75
|
+
psd.parse!
|
76
|
+
|
77
|
+
node = psd.tree.children.first
|
78
|
+
png = node.image.to_png_with_mask
|
79
|
+
|
80
|
+
expect(png).to be_an_instance_of(ChunkyPNG::Canvas)
|
81
|
+
expect(node.image.to_png_with_mask.__id__).to eq(png.__id__)
|
82
|
+
end
|
67
83
|
end
|
68
84
|
end
|
69
|
-
end
|
85
|
+
end
|
data/spec/locked_spec.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Locked' do
|
4
|
+
it "should parse locked layer info" do
|
5
|
+
psd = PSD.new('spec/files/locked.psd')
|
6
|
+
psd.parse!
|
7
|
+
|
8
|
+
expect(psd.tree.children[0].position_locked?).to eq(true)
|
9
|
+
expect(psd.tree.children[0].all_locked?).to eq(true)
|
10
|
+
expect(psd.tree.children[0].composite_locked?).to eq(true)
|
11
|
+
expect(psd.tree.children[0].transparency_locked?).to eq(true)
|
12
|
+
|
13
|
+
expect(psd.tree.children[1].position_locked?).to eq(true)
|
14
|
+
expect(psd.tree.children[1].all_locked?).to eq(false)
|
15
|
+
expect(psd.tree.children[1].composite_locked?).to eq(false)
|
16
|
+
expect(psd.tree.children[1].transparency_locked?).to eq(false)
|
17
|
+
|
18
|
+
expect(psd.tree.children[2].position_locked?).to eq(false)
|
19
|
+
expect(psd.tree.children[2].all_locked?).to eq(false)
|
20
|
+
expect(psd.tree.children[2].composite_locked?).to eq(true)
|
21
|
+
expect(psd.tree.children[2].transparency_locked?).to eq(false)
|
22
|
+
|
23
|
+
expect(psd.tree.children[3].position_locked?).to eq(false)
|
24
|
+
expect(psd.tree.children[3].all_locked?).to eq(false)
|
25
|
+
expect(psd.tree.children[3].composite_locked?).to eq(false)
|
26
|
+
expect(psd.tree.children[3].transparency_locked?).to eq(true)
|
27
|
+
|
28
|
+
expect(psd.tree.children[4].position_locked?).to eq(true)
|
29
|
+
expect(psd.tree.children[4].all_locked?).to eq(false)
|
30
|
+
expect(psd.tree.children[4].composite_locked?).to eq(true)
|
31
|
+
expect(psd.tree.children[4].transparency_locked?).to eq(false)
|
32
|
+
|
33
|
+
expect(psd.tree.children[5].position_locked?).to eq(true)
|
34
|
+
expect(psd.tree.children[5].all_locked?).to eq(false)
|
35
|
+
expect(psd.tree.children[5].composite_locked?).to eq(false)
|
36
|
+
expect(psd.tree.children[5].transparency_locked?).to eq(true)
|
37
|
+
|
38
|
+
expect(psd.tree.children[6].position_locked?).to eq(false)
|
39
|
+
expect(psd.tree.children[6].all_locked?).to eq(false)
|
40
|
+
expect(psd.tree.children[6].composite_locked?).to eq(false)
|
41
|
+
expect(psd.tree.children[6].transparency_locked?).to eq(false)
|
42
|
+
|
43
|
+
expect(psd.tree.children[7].position_locked?).to eq(true)
|
44
|
+
expect(psd.tree.children[7].all_locked?).to eq(true)
|
45
|
+
expect(psd.tree.children[7].composite_locked?).to eq(true)
|
46
|
+
expect(psd.tree.children[7].transparency_locked?).to eq(true)
|
47
|
+
|
48
|
+
expect(psd.tree.children[7].children[0].position_locked?).to eq(false)
|
49
|
+
expect(psd.tree.children[7].children[0].all_locked?).to eq(false)
|
50
|
+
expect(psd.tree.children[7].children[0].composite_locked?).to eq(false)
|
51
|
+
expect(psd.tree.children[7].children[0].transparency_locked?).to eq(false)
|
52
|
+
|
53
|
+
expect(psd.tree.children[7].children[1].position_locked?).to eq(true)
|
54
|
+
expect(psd.tree.children[7].children[1].all_locked?).to eq(true)
|
55
|
+
expect(psd.tree.children[7].children[1].composite_locked?).to eq(true)
|
56
|
+
expect(psd.tree.children[7].children[1].transparency_locked?).to eq(true)
|
57
|
+
|
58
|
+
expect(psd.tree.children[7].children[1].children[0].position_locked?).to eq(true)
|
59
|
+
expect(psd.tree.children[7].children[1].children[0].all_locked?).to eq(false)
|
60
|
+
expect(psd.tree.children[7].children[1].children[0].composite_locked?).to eq(false)
|
61
|
+
expect(psd.tree.children[7].children[1].children[0].transparency_locked?).to eq(false)
|
62
|
+
|
63
|
+
expect(psd.tree.children[8].position_locked?).to eq(true)
|
64
|
+
expect(psd.tree.children[8].all_locked?).to eq(false)
|
65
|
+
expect(psd.tree.children[8].composite_locked?).to eq(false)
|
66
|
+
expect(psd.tree.children[8].transparency_locked?).to eq(true)
|
67
|
+
|
68
|
+
expect(psd.tree.children[9].position_locked?).to eq(true)
|
69
|
+
expect(psd.tree.children[9].all_locked?).to eq(true)
|
70
|
+
expect(psd.tree.children[9].composite_locked?).to eq(true)
|
71
|
+
expect(psd.tree.children[9].transparency_locked?).to eq(true)
|
72
|
+
|
73
|
+
expect(psd.tree.children[12].position_locked?).to eq(false)
|
74
|
+
expect(psd.tree.children[12].all_locked?).to eq(false)
|
75
|
+
expect(psd.tree.children[12].composite_locked?).to eq(false)
|
76
|
+
expect(psd.tree.children[12].transparency_locked?).to eq(false)
|
77
|
+
end
|
78
|
+
end
|
data/spec/parsing_spec.rb
CHANGED
@@ -131,7 +131,7 @@ describe 'Parsing' do
|
|
131
131
|
expect(blend_mode.mode).to eq('normal')
|
132
132
|
expect(blend_mode.opacity).to eq(255)
|
133
133
|
expect(blend_mode.opacity_percentage).to eq(100)
|
134
|
-
expect(blend_mode.visible).to
|
134
|
+
expect(blend_mode.visible).to be true
|
135
135
|
end
|
136
136
|
|
137
137
|
it "should parse all layer comps" do
|
data/spec/slices_spec.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Slices' do
|
4
|
-
|
5
4
|
describe "Handle file with slice properly" do
|
6
|
-
|
7
|
-
|
8
5
|
it "should successfully parse a the PSD file of version 7 or above and has slices" do
|
9
6
|
psd = PSD.new('spec/files/slices.psd')
|
10
7
|
psd.parse!
|
@@ -21,11 +18,10 @@ describe 'Slices' do
|
|
21
18
|
# Bounds of each slice should not be nil
|
22
19
|
psd.resources[:slices].data.to_a.each do |slice|
|
23
20
|
expect(slice).to_not be_nil
|
24
|
-
expect(slice[
|
21
|
+
expect(slice[:bounds]).to_not be_nil
|
25
22
|
end
|
26
23
|
end
|
27
24
|
|
28
|
-
|
29
25
|
it "should successfully parse a the PSD file of version 6 and has slices" do
|
30
26
|
psd = PSD.new('spec/files/pixel.psd')
|
31
27
|
psd.parse!
|
@@ -39,14 +35,23 @@ describe 'Slices' do
|
|
39
35
|
# But slices version should be equal to 6
|
40
36
|
expect(psd.resources[:slices].data.version).to eq 6
|
41
37
|
|
42
|
-
#
|
43
|
-
|
38
|
+
# Bounds should not be nil
|
39
|
+
psd.resources[:slices].data.to_a.each do |slice|
|
40
|
+
expect(slice).to_not be_nil
|
41
|
+
expect(slice[:bounds]).to_not be_nil
|
42
|
+
end
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
47
46
|
describe "Handle file without slices properly" do
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
it "should successfully parse a PSD file which does not have slices" do
|
48
|
+
psd = PSD.new('spec/files/simplest.psd')
|
49
|
+
psd.parse!
|
50
|
+
|
51
|
+
expect(psd).to be_parsed
|
52
|
+
|
53
|
+
expect(psd.resources[:slices].data.to_a.size).to be 1
|
54
|
+
expect(psd.resources[:slices].data.to_a[0][:id]).to be 0
|
55
|
+
end
|
51
56
|
end
|
52
57
|
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: 1.
|
4
|
+
version: 1.5.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-
|
12
|
+
date: 2013-12-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -180,6 +180,7 @@ files:
|
|
180
180
|
- lib/psd/layer_info/layer_name_source.rb
|
181
181
|
- lib/psd/layer_info/layer_section_divider.rb
|
182
182
|
- lib/psd/layer_info/legacy_typetool.rb
|
183
|
+
- lib/psd/layer_info/locked.rb
|
183
184
|
- lib/psd/layer_info/metadata_setting.rb
|
184
185
|
- lib/psd/layer_info/object_effects.rb
|
185
186
|
- lib/psd/layer_info/placed_layer.rb
|
@@ -216,7 +217,9 @@ files:
|
|
216
217
|
- lib/psd/util.rb
|
217
218
|
- lib/psd/version.rb
|
218
219
|
- psd.gemspec
|
220
|
+
- spec/files/empty-layer.psd
|
219
221
|
- spec/files/example.psd
|
222
|
+
- spec/files/locked.psd
|
220
223
|
- spec/files/one_layer.psd
|
221
224
|
- spec/files/path.psd
|
222
225
|
- spec/files/pixel.psd
|
@@ -226,6 +229,7 @@ files:
|
|
226
229
|
- spec/hierarchy_spec.rb
|
227
230
|
- spec/image_spec.rb
|
228
231
|
- spec/lazy_execute_spec.rb
|
232
|
+
- spec/locked_spec.rb
|
229
233
|
- spec/parsing_spec.rb
|
230
234
|
- spec/psd_spec.rb
|
231
235
|
- spec/slices_spec.rb
|
@@ -256,7 +260,9 @@ signing_key:
|
|
256
260
|
specification_version: 4
|
257
261
|
summary: General purpose library for parsing Photoshop files
|
258
262
|
test_files:
|
263
|
+
- spec/files/empty-layer.psd
|
259
264
|
- spec/files/example.psd
|
265
|
+
- spec/files/locked.psd
|
260
266
|
- spec/files/one_layer.psd
|
261
267
|
- spec/files/path.psd
|
262
268
|
- spec/files/pixel.psd
|
@@ -266,6 +272,7 @@ test_files:
|
|
266
272
|
- spec/hierarchy_spec.rb
|
267
273
|
- spec/image_spec.rb
|
268
274
|
- spec/lazy_execute_spec.rb
|
275
|
+
- spec/locked_spec.rb
|
269
276
|
- spec/parsing_spec.rb
|
270
277
|
- spec/psd_spec.rb
|
271
278
|
- spec/slices_spec.rb
|