psd 1.2.2 → 1.3.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: 5a6c401c74ed1123fa74ad8b50dfc6a0df6da1e5
4
- data.tar.gz: c2092822eb4eae245fce20204cf05ff3f4bbe0df
3
+ metadata.gz: d04a7ff525ba486c5d37f11d89b6106c5ae9960a
4
+ data.tar.gz: 8993d2ce5b328983e55ee77a1ba6297afc37ad0a
5
5
  SHA512:
6
- metadata.gz: 3696d5e2ff46cff1496b739551d264ae5c7316e6cb6a8b74e8e99a37d4d757330d32badfe66b6d2b6da5dbca32ab871a8105cd72d5b0b43486b06541d01be835
7
- data.tar.gz: de41bf1813b74520558fc2161a4266d5f32b0ed7d6b26fda86ae55fbafbd2e2043f403ebcac4e23faa6441d81a8bd43f62c60e68c2b8232f9656d164d5cc65f9
6
+ metadata.gz: e5da04951ba02c95f1770574a70258736033ff3b9d8b74ede4873592c3ac102fe7972372df4e5ae8f5279d5044c7a475ac5f7de5e86611769d9db59234edf693
7
+ data.tar.gz: c2e37f8843ff642688c8ea136e28821c51ba8f964368026968301a69046eee757b7b4e48480fe91ff43bd18542d95814046fef9c07cee604aa6f25ad3466393f
data/README.md CHANGED
@@ -15,6 +15,7 @@ A general purpose Photoshop file parser written in Ruby. It allows you to work w
15
15
  * Color mode and bit-depth
16
16
  * Vector mask data
17
17
  * Flattened image data
18
+ * Layer comps
18
19
 
19
20
  PSD.rb is tested against:
20
21
 
@@ -90,6 +91,27 @@ For any of the traversal methods, you can also retrieve folder or layer nodes on
90
91
  psd.tree.descendant_layers
91
92
  ```
92
93
 
94
+ If you know the path to a group or layer within the tree, you can search by that path. Note that this always returns an Array because layer/group names do not have to be unique.
95
+
96
+ ``` ruby
97
+ psd.tree.children_at_path("Version A/Matte")
98
+ ```
99
+
100
+ **Layer Comps**
101
+
102
+ You can also filter nodes based on a layer comp. To generate a new tree consisting only of the layers that are enabled in a certain layer comp:
103
+
104
+ ``` ruby
105
+ # Get information about all the available layer comps
106
+ puts psd.layer_comps
107
+
108
+ # Can filter by name or by ID (obtained from above)
109
+ tree = psd.tree.filter_by_comp('Version A')
110
+ puts tree.children.map(&:name)
111
+ ```
112
+
113
+ This returns a new node tree and does not alter the original so you won't lose any data.
114
+
93
115
  **Accessing Layer Data**
94
116
 
95
117
  To get data such as the name or dimensions of a layer:
@@ -83,7 +83,7 @@ class PSD
83
83
 
84
84
  # Reads a boolean value.
85
85
  def read_boolean
86
- read(1)[0] != 0
86
+ read(1).bytes.to_a[0] != 0
87
87
  end
88
88
 
89
89
  # Reads a 32-bit color space value.
@@ -31,5 +31,9 @@ class PSD
31
31
  def tree
32
32
  @root ||= PSD::Node::Root.new(self)
33
33
  end
34
+
35
+ def layer_comps
36
+ @resources[:layer_comps].data.to_a
37
+ end
34
38
  end
35
39
  end
@@ -19,6 +19,7 @@ class PSD
19
19
  LAYER_INFO = {
20
20
  type: TypeTool,
21
21
  legacy_type: LegacyTypeTool,
22
+ metadata: MetadataSetting,
22
23
  layer_name_source: LayerNameSource,
23
24
  object_effects: ObjectEffects,
24
25
  name: UnicodeName,
@@ -0,0 +1,35 @@
1
+ require_relative '../layer_info'
2
+
3
+ class PSD
4
+ class MetadataSetting < LayerInfo
5
+ @key = 'shmd'
6
+
7
+ def parse
8
+ count = @file.read_int
9
+
10
+ count.times do
11
+ @file.seek 4, IO::SEEK_CUR # signature, always 8BIM
12
+
13
+ key = @file.read_string(4)
14
+ copy_on_sheet_dup = @file.read(1).bytes.to_a[0]
15
+ @file.seek 3, IO::SEEK_CUR # Padding
16
+
17
+ len = @file.read_int
18
+ data_end = @file.tell + len
19
+
20
+ PSD.logger.debug "Layer metadata: key = #{key}, length = #{len}"
21
+
22
+ parse_layer_comp_setting if key == 'cmls'
23
+
24
+ @file.seek data_end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def parse_layer_comp_setting
31
+ @file.seek 4, IO::SEEK_CUR # Version
32
+ @data[:layer_comp] = Descriptor.new(@file).parse
33
+ end
34
+ end
35
+ end
@@ -25,7 +25,7 @@ class PSD
25
25
  parser.parse!
26
26
  @data[:engine_data] = parser.result
27
27
  rescue Exception => e
28
- puts e.message
28
+ PSD.logger.error e.message
29
29
  end
30
30
 
31
31
  warpVersion = @file.read_short
@@ -11,7 +11,7 @@ class PSD
11
11
  # Default properties that all nodes contain
12
12
  PROPERTIES = [:name, :left, :right, :top, :bottom, :height, :width]
13
13
 
14
- attr_accessor :parent, :children
14
+ attr_accessor :parent, :children, :layer
15
15
 
16
16
  def initialize(layers=[])
17
17
  @children = []
@@ -29,6 +29,10 @@ class PSD
29
29
  @layer.visible?
30
30
  end
31
31
 
32
+ def psd
33
+ parent.psd
34
+ end
35
+
32
36
  def layer?
33
37
  is_a?(PSD::Node::Layer)
34
38
  end
@@ -55,6 +55,12 @@ class PSD::Node
55
55
  })
56
56
  end
57
57
 
58
+ # If the method is missing, we blindly send it to the layer.
59
+ # The layer handles the case in which the method doesn't exist.
60
+ def method_missing(method, *args, &block)
61
+ @layer.send(method, *args, &block)
62
+ end
63
+
58
64
  private
59
65
 
60
66
  def get_dimensions
@@ -25,12 +25,6 @@ class PSD::Node
25
25
  end
26
26
  end
27
27
 
28
- # If the method is missing, we blindly send it to the layer.
29
- # The layer handles the case in which the method doesn't exist.
30
- def method_missing(method, *args, &block)
31
- layer.send(method, *args, &block)
32
- end
33
-
34
28
  # Attempt to translate the layer.
35
29
  def translate(x=0, y=0)
36
30
  @layer.translate x, y
@@ -67,5 +61,11 @@ class PSD::Node
67
61
  ref_y: @layer.reference_point.y
68
62
  })
69
63
  end
64
+
65
+ # If the method is missing, we blindly send it to the layer.
66
+ # The layer handles the case in which the method doesn't exist.
67
+ def method_missing(method, *args, &block)
68
+ @layer.send(method, *args, &block)
69
+ end
70
70
  end
71
71
  end
@@ -6,7 +6,7 @@ class PSD::Node
6
6
  include PSD::HasChildren
7
7
  include PSD::Node::ParseLayers
8
8
 
9
- attr_reader :children
9
+ attr_accessor :children
10
10
 
11
11
  # Stores a reference to the parsed PSD and builds the
12
12
  # tree hierarchy.
@@ -51,6 +51,8 @@ class PSD::Node
51
51
  0
52
52
  end
53
53
 
54
+ def psd; @psd; end
55
+
54
56
  private
55
57
 
56
58
  def build_hierarchy
@@ -23,6 +23,41 @@ class PSD
23
23
  return matches.map { |m| m.children_at_path(path, opts) }.flatten
24
24
  end
25
25
  end
26
+
27
+ # Given a layer comp ID, name, or :last for last document state, create a new
28
+ # tree based on the layers/groups that belong to the comp only.
29
+ def filter_by_comp(id)
30
+ if id.is_a?(String)
31
+ comp = psd.layer_comps.select { |c| c[:name] == id }.first
32
+ raise "Layer comp not found" if comp.nil?
33
+
34
+ id = comp[:id]
35
+ elsif id == :last
36
+ id = 0
37
+ end
38
+
39
+ root = PSD::Node::Root.new(psd)
40
+ filter_for_comp!(id, root)
41
+
42
+ return root
43
+ end
44
+
45
+ private
46
+
47
+ def filter_for_comp!(id, node)
48
+ node.children.select! do |c|
49
+ c
50
+ .metadata
51
+ .data[:layer_comp]['layerSettings'].map { |l| !l.has_key?('enab') || l['enab'] ? l['compList'] : nil }
52
+ .flatten
53
+ .compact
54
+ .include?(id)
55
+ end
56
+
57
+ node.children.each do |c|
58
+ filter_for_comp!(id, c) if c.is_a?(PSD::Node::Group)
59
+ end
60
+ end
26
61
  end
27
62
  end
28
63
  end
@@ -20,6 +20,10 @@ class PSD
20
20
  def [](val)
21
21
  @data[val]
22
22
  end
23
+
24
+ def to_a
25
+ @data['list'].map { |c| {id: c['compID'], name: c['Nm '], captured_info: c['capturedInfo']} }
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -1,3 +1,3 @@
1
1
  class PSD
2
- VERSION = "1.2.2"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -86,6 +86,23 @@ describe "Hierarchy" do
86
86
  expect(@tree.children_at_path('NOPE')).to be_an_instance_of(Array)
87
87
  expect(@tree.children_at_path('NOPE').size).to eq(0)
88
88
  end
89
+
90
+ it "should throw an error if filtering by a non-existant layer comp" do
91
+ expect { @tree.filter_by_comp('WAT') }.to raise_error("Layer comp not found")
92
+ end
93
+
94
+ it "should correctly filter and produce a new tree when filtering by layer comp" do
95
+ tree = @tree.filter_by_comp('Version A')
96
+ expect(tree).to be_an_instance_of(PSD::Node::Root)
97
+ expect(tree.children.size).to eq(1)
98
+ expect(tree.children[0].name).to eq("Version A")
99
+ end
100
+
101
+ it "should return a new tree when filtering by layer comps" do
102
+ tree = @tree.filter_by_comp('Version A')
103
+ expect(tree).to_not eq(@tree)
104
+ expect(@tree.children.size).to eq(3)
105
+ end
89
106
  end
90
107
  end
91
108
  end
@@ -131,5 +131,18 @@ describe 'Parsing' do
131
131
  expect(blend_mode.opacity_percentage).to eq(100)
132
132
  expect(blend_mode.visible).to be_true
133
133
  end
134
+
135
+ it "should parse all layer comps" do
136
+ expect(@psd.layer_comps.size).to eq(3)
137
+ expect(@psd.layer_comps.map { |c| c[:name] }).to eq([
138
+ 'Version A',
139
+ 'Version B',
140
+ 'Version C'
141
+ ])
142
+
143
+ @psd.layer_comps.each do |c|
144
+ expect(c[:id]).to be > 0
145
+ end
146
+ end
134
147
  end
135
148
  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.2.2
4
+ version: 1.3.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-09-23 00:00:00.000000000 Z
12
+ date: 2013-09-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -165,6 +165,7 @@ files:
165
165
  - lib/psd/layer_info/layer_name_source.rb
166
166
  - lib/psd/layer_info/layer_section_divider.rb
167
167
  - lib/psd/layer_info/legacy_typetool.rb
168
+ - lib/psd/layer_info/metadata_setting.rb
168
169
  - lib/psd/layer_info/object_effects.rb
169
170
  - lib/psd/layer_info/placed_layer.rb
170
171
  - lib/psd/layer_info/reference_point.rb