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 +4 -4
- data/README.md +22 -0
- data/lib/psd.rb +26 -3
- data/lib/psd/descriptor.rb +10 -3
- data/lib/psd/resource.rb +14 -15
- data/lib/psd/resource_section.rb +22 -0
- data/lib/psd/resources.rb +25 -3
- data/lib/psd/resources/layer_comps.rb +16 -0
- data/lib/psd/version.rb +1 -1
- data/psd.gemspec +4 -1
- data/spec/parsing_spec.rb +4 -4
- data/spec/psd_spec.rb +29 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 041d9171afea1dcddd97b7466d804bf2caa39935
|
4
|
+
data.tar.gz: e8512840bae631128967d55a7df8ea34f4f56edc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
106
|
+
return @resources
|
84
107
|
end
|
85
108
|
|
86
109
|
# Get the LayerMask section. Ensures the header and resources
|
data/lib/psd/descriptor.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
data/lib/psd/resource.rb
CHANGED
@@ -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
|
7
|
-
|
6
|
+
class Resource
|
7
|
+
attr_reader :type, :id, :name, :size
|
8
|
+
attr_accessor :data
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def initialize(file)
|
11
|
+
@file = file
|
12
|
+
@data = {}
|
13
|
+
@type = nil
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
+
def parse
|
17
|
+
@type = @file.read_string(4) # Always 8BIM
|
18
|
+
@id = @file.read_short
|
16
19
|
|
17
|
-
|
18
|
-
|
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
|
-
|
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
|
data/lib/psd/resources.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/psd/version.rb
CHANGED
data/psd.gemspec
CHANGED
@@ -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
|
-
|
29
|
+
|
30
|
+
if RUBY_PLATFORM =~ /darwin/
|
31
|
+
gem.add_development_dependency 'rb-fsevent', '~> 0.9'
|
32
|
+
end
|
30
33
|
end
|
data/spec/parsing_spec.rb
CHANGED
@@ -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?(
|
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
|
data/spec/psd_spec.rb
ADDED
@@ -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.
|
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-
|
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
|