psd 0.3.5 → 0.4.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: 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