psd 1.4.5 → 1.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3c9491d68bacd47bd02b6ef1ebd95679de8f780d
4
- data.tar.gz: 3890b2b104af1b45d7754005cf91736cfd88c2fa
3
+ metadata.gz: 87dd8d12bd49feb7ad2502047fa2ec73d4aff7b1
4
+ data.tar.gz: 5647eca77e19f0a4ebb3ac59448d8b26d0f56da8
5
5
  SHA512:
6
- metadata.gz: 03d6f9c5332ca2b8ac5e981743e3955f0b26d5cd8f6b3ec7643602cebd4d7f0ac766190e1ca7d4e6618c9784d33d319a7ab8b548919bd491079096290ca74166
7
- data.tar.gz: e94c59785c2d9e6cac71e44e2f5a4efc8961e32da61f8382e36682af3a9ab6c58dba757a4411f383f87c6bc1a69cd9eaf9a5eedaa87ce4b2e6163b1d8e26a842
6
+ metadata.gz: d82f2fde33a13a60863548bc75788ffd279651e1c66a553749d794080733463e4f78cf13cfbcbf58a4d6296579f678cc3e32a0e6419c83ef7273e1b41d1406c5
7
+ data.tar.gz: 23a3d1ba099ed12c363958af463393e56702e2ac2a02ba67a9af247016fbe2f384f893f5ea5dad4bdb651cb76196f9a77d42efe69cf7ced445ea6177e4ffbffe
@@ -39,7 +39,6 @@ class PSD
39
39
  PSD.logger.debug @channels_info
40
40
 
41
41
  @chan_pos = 0
42
-
43
42
  @channels_info.each do |ch_info|
44
43
  @ch_info = ch_info
45
44
  if ch_info[:length] <= 0
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 opaque?(fg) || fully_transparent?(bg)
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).encode('UTF-8', 'MacRoman').delete("\000")
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
- png = ChunkyPNG::Canvas.new(width, height, ChunkyPNG::Color::TRANSPARENT)
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 >= png.width || offset_y >= png.height
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
- png[offset_x, offset_y] = @pixel_data[i]
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 >= png.width || offset_y >= png.height
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(png[offset_x, offset_y])
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
- png[offset_x, offset_y] = ChunkyPNG::Color.rgba(*color)
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, png.width)
67
- crop_top = PSD::Util.clamp(@layer.top, 0, png.height)
68
- crop_width = PSD::Util.clamp(@layer.width.to_i, 0, png.width - crop_left)
69
- crop_height = PSD::Util.clamp(@layer.height.to_i, 0, png.height - crop_top)
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
- png.crop!(crop_left, crop_top, crop_width, crop_height)
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 layer RLE channel ##{@ch_info[:id]}: position = #{@chan_pos}"
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
@@ -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
@@ -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
@@ -17,7 +17,7 @@ class PSD
17
17
 
18
18
  @data[:text] = Descriptor.new(@file).parse
19
19
  @data[:text]['EngineData']
20
- .encode!('UTF-8', 'MacRoman')
20
+ .encode!('UTF-8', 'MacRoman', invalid: :replace, undef: :replace)
21
21
  .delete!("\000")
22
22
 
23
23
  @data[:engine_data] = nil
@@ -55,7 +55,6 @@ class PSD
55
55
  end
56
56
 
57
57
  layers.each do |layer|
58
- @file.seek 8, IO::SEEK_CUR and next if layer.folder? || layer.folder_end?
59
58
  layer.parse_channel_image(@header)
60
59
  end
61
60
  end
@@ -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
- @left = @children.map(&:left).min || 0
68
- @top = @children.map(&:top).min || 0
69
- @bottom = @children.map(&:bottom).max || 0
70
- @right = @children.map(&:right).max || 0
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
@@ -52,6 +52,10 @@ class PSD::Node
52
52
  end
53
53
  end
54
54
 
55
+ def empty?
56
+ width == 0 || height == 0
57
+ end
58
+
55
59
  # Exports this layer to a Hash.
56
60
  def to_hash
57
61
  super.merge({
@@ -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.to_png
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
@@ -1,27 +1,132 @@
1
1
  class PSD
2
2
  class Resource
3
3
  class Section
4
- class Slices < Section
5
- attr_reader :data, :version
4
+ class Slices < Section
5
+ attr_reader :data, :version
6
6
 
7
- def self.id; 1050; end
8
- def self.name; :slices; end
7
+ def self.id; 1050; end
8
+ def self.name; :slices; end
9
9
 
10
- def parse
11
- @version = @file.read_int
12
- @descriptor_version = @file.read_int
13
- @resource.data = self
10
+ def parse
11
+ @version = @file.read_int
14
12
 
15
- if @version == 7 || @version == 8
16
- @data = Descriptor.new(@file).parse
17
- end
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
- def to_a
22
- @data.nil? ? [] : @data['slices']
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
@@ -1,3 +1,3 @@
1
1
  class PSD
2
- VERSION = "1.4.5"
2
+ VERSION = "1.5.0"
3
3
  end
Binary file
Binary file
@@ -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 be_true
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
- before(:each) do
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
- @psd.parse!
15
- expect(@psd).to be_parsed
16
- expect(@psd.image).to_not be_nil
17
- expect(@psd.image.width).to eq(1)
18
- expect(@psd.image.height).to eq(1)
19
- expect(@psd.image.pixel_data).to eq([ChunkyPNG::Color.rgba(0, 100, 200, 255)])
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(@psd).to_not be_parsed
24
- expect(@psd.image.width).to eq(1)
25
- expect(@psd.image.height).to eq(1)
26
- expect(@psd.image.pixel_data).to eq([ChunkyPNG::Color.rgba(0, 100, 200, 255)])
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(@psd.image.to_png).to be_an_instance_of(ChunkyPNG::Canvas)
25
+ expect(psd.image.to_png).to be_an_instance_of(ChunkyPNG::Canvas)
32
26
 
33
- expect(@psd.image.to_png.width).to eq(1)
34
- expect(@psd.image.to_png.height).to eq(1)
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(@psd.image.to_png[0,0])
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
- @psd.options[:parse_layer_images] = true
45
- @psd.parse!
38
+ psd.options[:parse_layer_images] = true
39
+ psd.parse!
46
40
 
47
- image = @psd.tree.children.first.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
- @psd.options[:parse_layer_images] = true
57
- @psd.parse!
50
+ psd.options[:parse_layer_images] = true
51
+ psd.parse!
58
52
 
59
- png = @psd.tree.children.first.image.to_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
@@ -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 be_true
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['bounds']).to_not be_nil
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
- # Slices data to be nil
43
- expect(psd.resources[:slices].data.to_a).to eq []
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
- #TODO: All files have atleast one autogenerated slice. Dunno how to handle this case yet!
49
- #it "should successfully parse a PSD file which does not have slices" do
50
- #end
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.5
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-11-26 00:00:00.000000000 Z
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