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 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