psd 0.3.5 → 0.4.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: 3ad5fbd51101b2362d57e17fe0fe5544ebc51ea8
4
- data.tar.gz: bb899376edd469fd83536d6bf2b50b96fb044b6e
3
+ metadata.gz: 041d9171afea1dcddd97b7466d804bf2caa39935
4
+ data.tar.gz: e8512840bae631128967d55a7df8ea34f4f56edc
5
5
  SHA512:
6
- metadata.gz: 92068cabd35464e01395b673cc8a4322fd0cd5e8f9317fcc33571df8cea9d0bd59cc98e4faeeca91399634f5549c3b04b4fbf3f891d5b32a154a54843a46e98d
7
- data.tar.gz: 26686e5b31b29de64da12dce875cc9edde976451028cd0ccd1f25c855db2851e00be26619c4a8d49e84d8ee076b05e3015acd92bb978a24b014203489890b4ea
6
+ metadata.gz: 88b090498daaa95c0c78235a513d37f790622d03d0de4340fa02ac662c1a3dc2a9bbb9d622bb85bbda21c1b277f7b10056555be09178208ac3c784815ebcc694
7
+ data.tar.gz: 25edcad79447dd549a89feb77ed63f513778bdbe890dcdb76a9a92f3fc24da14ba2b499d93eb1f6ee447bf24fd1ce35dd062aa1172cff730cec3fe891769ffaa
data/README.md CHANGED
@@ -41,6 +41,28 @@ psd = PSD.new('/path/to/file.psd')
41
41
  psd.parse!
42
42
  ```
43
43
 
44
+ Or, if you prefer the File.open way of doing things, you can do that too.
45
+
46
+ ``` ruby
47
+ require 'psd'
48
+
49
+ PSD.open('path/to/file.psd') do |psd|
50
+ p psd.tree.to_hash
51
+ end
52
+ ```
53
+
54
+ As you can see, `open` calls `parse!` for you, so that you can get down to business right away.
55
+
56
+ If you happen to prefer things DSL-style, the `open` method will also let you operate on the PSD object directly. Again, the call to `parse!` is handled for you.
57
+
58
+ ``` ruby
59
+ require 'psd'
60
+
61
+ PSD.open('path/to/file.psd') do
62
+ p tree.to_hash
63
+ end
64
+ ```
65
+
44
66
  **Traversing the Document**
45
67
 
46
68
  To access the document as a tree structure, use `psd.tree` to get the root node. From there, you can traverse the tree using any of these methods:
data/lib/psd.rb CHANGED
@@ -30,6 +30,24 @@ class PSD
30
30
 
31
31
  attr_reader :file
32
32
 
33
+ # Opens the named file, parses it, and makes it available for reading. Then, closes it after you're finished.
34
+ # @param filename [String] the name of the file to open
35
+ # @return [PSD] the {PSD} object if no block was given, otherwise the value of the block
36
+ def self.open(filename, opts={}, &block)
37
+ psd = PSD.new(filename, opts)
38
+ psd.parse!
39
+
40
+ return psd unless block_given?
41
+
42
+ if 0 == block.arity
43
+ psd.instance_eval(&block)
44
+ else
45
+ yield psd
46
+ end
47
+ ensure
48
+ psd.close if psd
49
+ end
50
+
33
51
  # Create and store a reference to our PSD file
34
52
  def initialize(file, opts={})
35
53
  @file = PSD::File.new(file, 'rb')
@@ -42,6 +60,11 @@ class PSD
42
60
  @parsed = false
43
61
  end
44
62
 
63
+ # Close the PSD file
64
+ def close
65
+ file.close unless file.closed?
66
+ end
67
+
45
68
  # There is a specific order that must be followed when parsing
46
69
  # the PSD. Sections can be skipped if needed. This method will
47
70
  # parse all sections of the PSD.
@@ -71,16 +94,16 @@ class PSD
71
94
 
72
95
  # Get the Resources section, parsing if needed.
73
96
  def resources
74
- return @resources.data unless @resources.nil?
97
+ return @resources unless @resources.nil?
75
98
 
76
99
  ensure_header
77
100
 
78
101
  @resources = Resources.new(@file)
79
102
  @resources.parse
80
103
 
81
- PSD.logger.debug @resources.inspect
104
+ PSD.logger.debug @resources.data.inspect
82
105
 
83
- return @resources.data
106
+ return @resources
84
107
  end
85
108
 
86
109
  # Get the LayerMask section. Ensures the header and resources
@@ -13,9 +13,13 @@ class PSD
13
13
  # a variable number of items in the descriptor. We return the Hash that represents
14
14
  # the full data structure.
15
15
  def parse
16
- @data[:class] = parse_class
16
+ PSD.logger.debug "Descriptor: pos = #{@file.tell}"
17
17
 
18
+ @data[:class] = parse_class
18
19
  num_items = @file.read_int
20
+
21
+ PSD.logger.debug "Class = #{@data[:class]}, Item count = #{num_items}"
22
+
19
23
  num_items.times do |i|
20
24
  id, value = parse_key_item
21
25
  @data[id] = value
@@ -35,11 +39,13 @@ class PSD
35
39
 
36
40
  def parse_id
37
41
  len = @file.read_int
38
- len == 0 ? @file.read_int : @file.read_string(len)
42
+ len == 0 ? @file.read_string(4) : @file.read_string(len)
39
43
  end
40
44
 
41
45
  def parse_key_item
42
46
  id = parse_id
47
+ PSD.logger.debug "Key = #{id}"
48
+
43
49
  value = parse_item
44
50
 
45
51
  return id, value
@@ -47,11 +53,12 @@ class PSD
47
53
 
48
54
  def parse_item(type = nil)
49
55
  type = @file.read_string(4) if type.nil?
56
+ PSD.logger.debug "Type = #{type}"
50
57
 
51
58
  value = case type
52
59
  when 'bool' then parse_boolean
53
60
  when 'type', 'GlbC' then parse_class
54
- when 'Objc', 'GlbO' then parse
61
+ when 'Objc', 'GlbO' then Descriptor.new(@file).parse
55
62
  when 'doub' then parse_double
56
63
  when 'enum' then parse_enum
57
64
  when 'alis' then parse_alias
@@ -3,25 +3,24 @@ class PSD
3
3
  #
4
4
  # Most of the resources are options/preferences set by the user
5
5
  # or automatically by Photoshop.
6
- class Resource < BinData::Record
7
- endian :big
6
+ class Resource
7
+ attr_reader :type, :id, :name, :size
8
+ attr_accessor :data
8
9
 
9
- string :type, read_length: 4
10
- uint16 :id
11
- uint8 :name_len
12
- stringz :name, read_length: :name_length
13
- uint32 :res_size
10
+ def initialize(file)
11
+ @file = file
12
+ @data = {}
13
+ @type = nil
14
+ end
14
15
 
15
- skip length: :resource_size
16
+ def parse
17
+ @type = @file.read_string(4) # Always 8BIM
18
+ @id = @file.read_short
16
19
 
17
- #---
18
- # Really weird padding business
19
- def name_length
20
- Util.pad2(name_len + 1) - 1
21
- end
20
+ name_length = Util.pad2(@file.read(1).bytes.to_a[0] + 1) - 1
21
+ @name = @file.read_string(name_length)
22
22
 
23
- def resource_size
24
- Util.pad2(res_size)
23
+ @size = Util.pad2(@file.read_int)
25
24
  end
26
25
  end
27
26
  end
@@ -0,0 +1,22 @@
1
+ class PSD
2
+ class Resource
3
+ class Section
4
+ def self.factory(file, resource)
5
+ Section.constants.each do |c|
6
+ section = Section.const_get(c)
7
+ next unless section.id == resource.id
8
+
9
+ section.new(file, resource).parse
10
+ return section.name
11
+ end
12
+
13
+ return nil
14
+ end
15
+
16
+ def initialize(file, resource)
17
+ @file = file
18
+ @resource = resource
19
+ end
20
+ end
21
+ end
22
+ end
@@ -8,7 +8,8 @@ class PSD
8
8
 
9
9
  def initialize(file)
10
10
  @file = file
11
- @resources = []
11
+ @resources = {}
12
+ @type_index = {}
12
13
  @length = nil
13
14
  end
14
15
 
@@ -21,7 +22,17 @@ class PSD
21
22
 
22
23
  while n > 0
23
24
  pos = @file.tell
24
- @resources << PSD::Resource.read(@file)
25
+
26
+ resource = Resource.new(@file)
27
+ resource.parse
28
+
29
+ resource_end = @file.tell + resource.size
30
+
31
+ name = Resource::Section.factory(@file, resource)
32
+ @resources[resource.id] = resource
33
+ @type_index[name] = resource.id unless name.nil?
34
+
35
+ @file.seek resource_end
25
36
  n -= @file.tell - pos
26
37
  end
27
38
 
@@ -30,13 +41,24 @@ class PSD
30
41
  end
31
42
 
32
43
  end_section
33
- return @resources
34
44
  end
35
45
 
36
46
  def skip
37
47
  @file.seek length, IO::SEEK_CUR
38
48
  end
39
49
 
50
+ def [](id)
51
+ if id.is_a?(Symbol)
52
+ by_type(id)
53
+ else
54
+ @resources[id]
55
+ end
56
+ end
57
+
58
+ def by_type(id)
59
+ @resources[@type_index[id]]
60
+ end
61
+
40
62
  private
41
63
 
42
64
  def length
@@ -0,0 +1,16 @@
1
+ class PSD
2
+ class Resource
3
+ class Section
4
+ class LayerComps < Section
5
+ def self.id; 1065; end
6
+ def self.name; :layer_comps; end
7
+
8
+ def parse
9
+ # Descriptor version
10
+ @file.seek 4, IO::SEEK_CUR
11
+ @resource.data = Descriptor.new(@file).parse
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  class PSD
2
- VERSION = "0.3.5"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -26,5 +26,8 @@ Gem::Specification.new do |gem|
26
26
  gem.add_development_dependency 'rspec'
27
27
  gem.add_development_dependency 'guard'
28
28
  gem.add_development_dependency 'guard-rspec'
29
- gem.add_development_dependency 'rb-fsevent', '~> 0.9'
29
+
30
+ if RUBY_PLATFORM =~ /darwin/
31
+ gem.add_development_dependency 'rb-fsevent', '~> 0.9'
32
+ end
30
33
  end
@@ -45,16 +45,16 @@ describe 'Parsing' do
45
45
 
46
46
  it "should contain data" do
47
47
  @psd.resources.should_not be_nil
48
- @psd.resources.is_a?(Array).should be_true
49
- @psd.resources.size.should >= 1
48
+ @psd.resources.data.is_a?(Hash).should be_true
49
+ @psd.resources.data.size.should >= 1
50
50
  end
51
51
 
52
52
  it "should be of type 8BIM" do
53
- @psd.resources.each { |r| r.type.should == '8BIM' }
53
+ @psd.resources.data.each { |id, r| r.type.should == '8BIM' }
54
54
  end
55
55
 
56
56
  it "should have an ID" do
57
- @psd.resources.each do |r|
57
+ @psd.resources.data.each do |id, r|
58
58
  r.id.should_not be_nil
59
59
  end
60
60
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'PSD' do
4
+ let(:filename) { 'spec/files/example.psd' }
5
+
6
+ it 'should open a file without a block' do
7
+ psd = PSD.open(filename)
8
+ psd.parsed?.should == true
9
+ psd.should be_instance_of PSD
10
+ end
11
+
12
+ it 'should refuse to open a bad filename' do
13
+ expect { PSD.open('') }.to raise_error
14
+ end
15
+
16
+ it 'should open a file and feed it to a block' do
17
+ PSD.open(filename) do |psd|
18
+ psd.parsed?.should == true
19
+ psd.should be_instance_of PSD
20
+ end
21
+ end
22
+
23
+ it 'should open a file and feed it to a block DSL style' do
24
+ PSD.open(filename) do
25
+ parsed?.should == true
26
+ is_a?(PSD).should == true
27
+ end
28
+ end
29
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: psd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan LeFevre
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-02 00:00:00.000000000 Z
12
+ date: 2013-08-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bindata
@@ -170,7 +170,9 @@ files:
170
170
  - lib/psd/pascal_string.rb
171
171
  - lib/psd/path_record.rb
172
172
  - lib/psd/resource.rb
173
+ - lib/psd/resource_section.rb
173
174
  - lib/psd/resources.rb
175
+ - lib/psd/resources/layer_comps.rb
174
176
  - lib/psd/section.rb
175
177
  - lib/psd/util.rb
176
178
  - lib/psd/version.rb
@@ -183,6 +185,7 @@ files:
183
185
  - spec/hierarchy_spec.rb
184
186
  - spec/identity_spec.rb
185
187
  - spec/parsing_spec.rb
188
+ - spec/psd_spec.rb
186
189
  - spec/spec_helper.rb
187
190
  - spec/text_spec.rb
188
191
  homepage: http://cosmos.layervault.com/psdrb.html
@@ -218,5 +221,6 @@ test_files:
218
221
  - spec/hierarchy_spec.rb
219
222
  - spec/identity_spec.rb
220
223
  - spec/parsing_spec.rb
224
+ - spec/psd_spec.rb
221
225
  - spec/spec_helper.rb
222
226
  - spec/text_spec.rb