psd 1.3.2 → 1.3.3

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: 07d04a8de80525605f55cc1346b195d159448da4
4
- data.tar.gz: 0eb80cec71da604ffa67e727ad90377b881dd028
3
+ metadata.gz: 448518141d3b745904f809280d0e826e08bf8dc0
4
+ data.tar.gz: 4c0725157152e2f924b95cf023603a9b86bd1f54
5
5
  SHA512:
6
- metadata.gz: 5dab1760c194b6abffe95ee77d061987dfb1d0cf8e7c59375d2f78726c912d36ef786fd5e240d7305d8edefe2f2650fea1546bdfc55b41ce2620d142383ed251
7
- data.tar.gz: e04a6725116b6be409215c47fb1d208c7aaacdb4959d84cbc9390d43cd9f2f96378858ac31880be427a02ada478320523a7cec37923bdc7b914d15f7e9d47197
6
+ metadata.gz: e559032f4b160eb26451c38aa4bc3fa7869aa6984f60c5d062596087cf32a38fdc181e3c703a56b22ba88fc965abe727fa158eb427f76042e46c55f694593ab3
7
+ data.tar.gz: 4e31eb123c902d3381e3895d262589f8d269aca4e30c8a74f541d03ef6a9e527aefc9c895df5e358f62e5e8f5b584e4a6a5070b4cf83a10892c629ba46550d96
data/lib/psd.rb CHANGED
@@ -9,7 +9,8 @@ dir_root = File.dirname(File.absolute_path(__FILE__)) + '/psd'
9
9
  '/image_modes/*',
10
10
  '/image_exports/*',
11
11
  '/nodes/*',
12
- '/layer_info/**/*',
12
+ '/layer_info/*',
13
+ '/layer/*',
13
14
  '/**/*'
14
15
  ].each do |path|
15
16
  Dir.glob(dir_root + path) { |file| require file if File.file?(file) }
@@ -3,38 +3,24 @@ class PSD
3
3
  # that layer.
4
4
  class Layer
5
5
  include Section
6
-
7
- attr_reader :id, :mask, :blending_ranges, :adjustments, :channels_info
8
- attr_reader :blend_mode, :layer_type, :blending_mode, :opacity, :fill_opacity
9
- attr_reader :channels, :image
10
-
11
- attr_accessor :group_layer
12
- attr_accessor :top, :left, :bottom, :right, :rows, :cols, :ref_x, :ref_y, :node, :file
13
-
14
- alias :info :adjustments
15
- alias :width :cols
16
- alias :height :rows
17
-
18
- # All of the extra layer info sections that we know how to parse.
19
- LAYER_INFO = {
20
- type: TypeTool,
21
- legacy_type: LegacyTypeTool,
22
- metadata: MetadataSetting,
23
- layer_name_source: LayerNameSource,
24
- object_effects: ObjectEffects,
25
- name: UnicodeName,
26
- section_divider: LayerSectionDivider,
27
- reference_point: ReferencePoint,
28
- layer_id: LayerID,
29
- fill_opacity: FillOpacity,
30
- placed_layer: PlacedLayer,
31
- vector_mask: VectorMask
32
- }
6
+ include BlendModes
7
+ include BlendingRanges
8
+ include ChannelImage
9
+ include Exporting
10
+ include Helpers
11
+ include Info
12
+ include Mask
13
+ include Name
14
+ include PathComponents
15
+ include PositionAndChannels
16
+
17
+ attr_reader :id, :info_keys
18
+ attr_accessor :group_layer, :node, :file
33
19
 
34
20
  # Initializes all of the defaults for the layer.
35
21
  def initialize(file)
36
22
  @file = file
37
- @image = nil
23
+
38
24
  @mask = {}
39
25
  @blending_ranges = {}
40
26
  @adjustments = {}
@@ -42,10 +28,8 @@ class PSD
42
28
  @blend_mode = {}
43
29
  @group_layer = nil
44
30
 
45
- @layer_type = 'normal'
46
31
  @blending_mode = 'normal'
47
32
  @opacity = 255
48
- @fill_opacity = 255
49
33
 
50
34
  # Just used for tracking which layer adjustments we're parsing.
51
35
  # Not essential.
@@ -56,9 +40,9 @@ class PSD
56
40
  def parse(index=nil)
57
41
  start_section
58
42
 
59
- @idx = index
43
+ @id = index
60
44
 
61
- parse_info
45
+ parse_position_and_channels
62
46
  parse_blend_modes
63
47
 
64
48
  extra_len = @file.read_int
@@ -67,7 +51,7 @@ class PSD
67
51
  parse_mask_data
68
52
  parse_blending_ranges
69
53
  parse_legacy_layer_name
70
- parse_extra_data
54
+ parse_layer_info
71
55
 
72
56
  PSD.logger.debug "Layer name = #{name}"
73
57
 
@@ -77,278 +61,16 @@ class PSD
77
61
  return self
78
62
  end
79
63
 
80
- # Export the layer to file. May or may not work.
81
- def export(outfile)
82
- export_info(outfile)
83
-
84
- @blend_mode.write(outfile)
85
- @file.seek(@blend_mode.num_bytes, IO::SEEK_CUR)
86
-
87
- export_mask_data(outfile)
88
- export_blending_ranges(outfile)
89
- export_legacy_layer_name(outfile)
90
- export_extra_data(outfile)
91
-
92
- outfile.write @file.read(end_of_section - @file.tell)
93
- end
94
-
95
64
  # We just delegate this to a normal method call.
96
65
  def [](val)
97
66
  self.send(val)
98
67
  end
99
68
 
100
- def parse_channel_image!(header, parse)
101
- @image = ChannelImage.new(@file, header, self)
102
- parse ? @image.parse : @image.skip
103
- end
104
-
105
- # Does this layer represent the start of a folder section?
106
- def folder?
107
- return false unless @adjustments.has_key?(:section_divider)
108
- @adjustments[:section_divider].is_folder
109
- end
110
-
111
- # Does this layer represent the end of a folder section?
112
- def folder_end?
113
- return false unless @adjustments.has_key?(:section_divider)
114
- @adjustments[:section_divider].is_hidden
115
- end
116
-
117
- # Is this layer visible?
118
- def visible?
119
- @visible
120
- end
121
-
122
- # Is this layer hidden?
123
- def hidden?
124
- !@visible
125
- end
126
-
127
- # Attempt to translate this layer and modify the document.
128
- def translate(x=0, y=0)
129
- @left += x
130
- @right += x
131
- @top += y
132
- @bottom += y
133
-
134
- @path_components.each{ |p| p.translate(x,y) } if @path_components
135
- end
136
-
137
- # Attempt to scale the path components within this layer.
138
- def scale_path_components(xr, yr)
139
- return unless @path_components
140
-
141
- @path_components.each{ |p| p.scale(xr, yr) }
142
- end
143
-
144
- # Helper that exports the text data in this layer, if any.
145
- def text
146
- return nil unless @adjustments[:type]
147
- @adjustments[:type].to_hash
148
- end
149
-
150
- # Gets the name of this layer. If the PSD file is from an even remotely
151
- # recent version of Photoshop, this data is stored as extra layer info and
152
- # as a UTF-16 name. Otherwise, it's stored in a legacy block.
153
- def name
154
- if @adjustments.has_key?(:name)
155
- return @adjustments[:name].data
156
- end
157
-
158
- return @legacy_name
159
- end
160
-
161
69
  # We delegate all missing method calls to the extra layer info to make it easier
162
70
  # to access that data.
163
71
  def method_missing(method, *args, &block)
164
72
  return @adjustments[method] if @adjustments.has_key?(method)
165
73
  super
166
74
  end
167
-
168
- private
169
-
170
- def parse_info
171
- start_section(:info)
172
-
173
- @top = @file.read_int
174
- @left = @file.read_int
175
- @bottom = @file.read_int
176
- @right = @file.read_int
177
- @channels = @file.read_short
178
-
179
- @rows = @bottom - @top
180
- @cols = @right - @left
181
-
182
- @channels.times do
183
- channel_id = @file.read_short
184
- channel_length = @file.read_int
185
-
186
- @channels_info << {id: channel_id, length: channel_length}
187
- end
188
-
189
- end_section(:info)
190
- end
191
-
192
- def export_info(outfile)
193
- [@top, @left, @bottom, @right].each { |val| outfile.write_int(val) }
194
- outfile.write_short(@channels)
195
-
196
- @channels_info.each do |channel_info|
197
- outfile.write_short channel_info[:id]
198
- outfile.write_int channel_info[:length]
199
- end
200
-
201
- @file.seek end_of_section(:info)
202
- end
203
-
204
- def export_mask_data(outfile)
205
- outfile.write @file.read(@mask_end - @mask_begin + 4)
206
- end
207
-
208
- def export_blending_ranges(outfile)
209
- length = 4 * 2 # greys
210
- length += @blending_ranges[:num_channels] * 8
211
- outfile.write_int length
212
-
213
- outfile.write_short @blending_ranges[:grey][:source][:black]
214
- outfile.write_short @blending_ranges[:grey][:source][:white]
215
- outfile.write_short @blending_ranges[:grey][:dest][:black]
216
- outfile.write_short @blending_ranges[:grey][:dest][:white]
217
-
218
- @blending_ranges[:num_channels].times do |i|
219
- outfile.write_short @blending_ranges[:channels][i][:source][:black]
220
- outfile.write_short @blending_ranges[:channels][i][:source][:white]
221
- outfile.write_short @blending_ranges[:channels][i][:dest][:black]
222
- outfile.write_short @blending_ranges[:channels][i][:dest][:white]
223
- end
224
-
225
- @file.seek length + 4, IO::SEEK_CUR
226
- end
227
-
228
- def export_legacy_layer_name(outfile)
229
- outfile.write @file.read(@legacy_name_end - @legacy_name_start)
230
- end
231
-
232
- def export_extra_data(outfile)
233
- outfile.write @file.read(@extra_data_end - @extra_data_begin)
234
- if @path_components && !@path_components.empty?
235
- outfile.seek @vector_mask_begin
236
- @file.seek @vector_mask_begin
237
-
238
- write_vector_mask(outfile)
239
- @file.seek outfile.tell
240
- end
241
- end
242
-
243
- def parse_blend_modes
244
- @blend_mode = BlendMode.read(@file)
245
-
246
- @blending_mode = @blend_mode.mode
247
- @opacity = @blend_mode.opacity
248
- @visible = @blend_mode.visible
249
- end
250
-
251
- def parse_mask_data
252
- @mask_begin = @file.tell
253
- @mask = Mask.read(@file)
254
- @mask_end = @file.tell
255
- end
256
-
257
- def parse_blending_ranges
258
- length = @file.read_int
259
-
260
- @blending_ranges[:grey] = {
261
- source: {
262
- black: @file.read_short,
263
- white: @file.read_short
264
- },
265
- dest: {
266
- black: @file.read_short,
267
- white: @file.read_short
268
- }
269
- }
270
-
271
- @blending_ranges[:num_channels] = (length - 8) / 8
272
-
273
- @blending_ranges[:channels] = []
274
- @blending_ranges[:num_channels].times do
275
- @blending_ranges[:channels] << {
276
- source: {
277
- black: @file.read_short,
278
- white: @file.read_short
279
- },
280
- dest: {
281
- black: @file.read_short,
282
- white: @file.read_short
283
- }
284
- }
285
- end
286
- end
287
-
288
- # The old school layer names are encoded in MacRoman format,
289
- # not UTF-8. Luckily Ruby kicks ass at character conversion.
290
- def parse_legacy_layer_name
291
- @legacy_name_start = @file.tell
292
-
293
- len = Util.pad4 @file.read(1).bytes.to_a[0]
294
- @legacy_name = @file.read_string(len)
295
-
296
- @legacy_name_end = @file.tell
297
- end
298
-
299
- # This section is a bit tricky to parse because it represents all of the
300
- # extra data that describes this layer.
301
- def parse_extra_data
302
- @extra_data_begin = @file.tell
303
-
304
- while @file.tell < @layer_end
305
- # Signature, don't need
306
- @file.seek 4, IO::SEEK_CUR
307
-
308
- # Key, very important
309
- key = @file.read_string(4)
310
- @info_keys << key
311
-
312
- length = Util.pad2 @file.read_int
313
- pos = @file.tell
314
-
315
- info_parsed = false
316
- LAYER_INFO.each do |name, info|
317
- next unless info.key == key
318
-
319
- PSD.logger.debug "Layer Info: key = #{key}, start = #{pos}, length = #{length}"
320
-
321
- begin
322
- i = info.new(@file, length)
323
- i.parse
324
-
325
- @adjustments[name] = i
326
- info_parsed = true
327
- rescue Exception => e
328
- PSD.logger.error "Parsing error: key = #{key}, message = #{e.message}"
329
- PSD.logger.error e.backtrace.join("\n")
330
- end
331
-
332
- break
333
- end
334
-
335
- if !info_parsed
336
- PSD.logger.debug "Skipping: key = #{key}, pos = #{@file.tell}, length = #{length}"
337
- @file.seek pos + length
338
- end
339
-
340
- @file.seek pos + length if @file.tell != (pos + length)
341
- end
342
-
343
- @extra_data_end = @file.tell
344
- end
345
-
346
- def write_vector_mask(outfile)
347
- outfile.write @file.read(8)
348
- # outfile.write_int 3
349
- # outfile.write_int @vector_tag
350
-
351
- @path_components.each{ |pc| pc.write(outfile); @file.seek(26, IO::SEEK_CUR) }
352
- end
353
75
  end
354
76
  end
@@ -0,0 +1,17 @@
1
+ class PSD
2
+ class Layer
3
+ module BlendModes
4
+ attr_reader :blend_mode, :blending_mode, :opacity
5
+
6
+ private
7
+
8
+ def parse_blend_modes
9
+ @blend_mode = BlendMode.read(@file)
10
+
11
+ @blending_mode = @blend_mode.mode
12
+ @opacity = @blend_mode.opacity
13
+ @visible = @blend_mode.visible
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,60 @@
1
+ class PSD
2
+ class Layer
3
+ module BlendingRanges
4
+ attr_reader :blending_ranges
5
+
6
+ private
7
+
8
+ def parse_blending_ranges
9
+ length = @file.read_int
10
+
11
+ @blending_ranges[:grey] = {
12
+ source: {
13
+ black: @file.read_short,
14
+ white: @file.read_short
15
+ },
16
+ dest: {
17
+ black: @file.read_short,
18
+ white: @file.read_short
19
+ }
20
+ }
21
+
22
+ @blending_ranges[:num_channels] = (length - 8) / 8
23
+
24
+ @blending_ranges[:channels] = []
25
+ @blending_ranges[:num_channels].times do
26
+ @blending_ranges[:channels] << {
27
+ source: {
28
+ black: @file.read_short,
29
+ white: @file.read_short
30
+ },
31
+ dest: {
32
+ black: @file.read_short,
33
+ white: @file.read_short
34
+ }
35
+ }
36
+ end
37
+ end
38
+
39
+ def export_blending_ranges(outfile)
40
+ length = 4 * 2 # greys
41
+ length += @blending_ranges[:num_channels] * 8
42
+ outfile.write_int length
43
+
44
+ outfile.write_short @blending_ranges[:grey][:source][:black]
45
+ outfile.write_short @blending_ranges[:grey][:source][:white]
46
+ outfile.write_short @blending_ranges[:grey][:dest][:black]
47
+ outfile.write_short @blending_ranges[:grey][:dest][:white]
48
+
49
+ @blending_ranges[:num_channels].times do |i|
50
+ outfile.write_short @blending_ranges[:channels][i][:source][:black]
51
+ outfile.write_short @blending_ranges[:channels][i][:source][:white]
52
+ outfile.write_short @blending_ranges[:channels][i][:dest][:black]
53
+ outfile.write_short @blending_ranges[:channels][i][:dest][:white]
54
+ end
55
+
56
+ @file.seek length + 4, IO::SEEK_CUR
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,12 @@
1
+ class PSD
2
+ class Layer
3
+ module ChannelImage
4
+ attr_reader :image
5
+
6
+ def parse_channel_image(header, parse)
7
+ @image = PSD::ChannelImage.new(@file, header, self)
8
+ parse ? @image.parse : @image.skip
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ class PSD
2
+ class Layer
3
+ module Exporting
4
+ # Export the layer to file. May or may not work.
5
+ def export(outfile)
6
+ export_position_and_channels(outfile)
7
+
8
+ @blend_mode.write(outfile)
9
+ @file.seek(@blend_mode.num_bytes, IO::SEEK_CUR)
10
+
11
+ export_mask_data(outfile)
12
+ export_blending_ranges(outfile)
13
+ export_legacy_layer_name(outfile)
14
+ export_extra_data(outfile)
15
+
16
+ outfile.write @file.read(end_of_section - @file.tell)
17
+ end
18
+
19
+ def write_vector_mask(outfile)
20
+ outfile.write @file.read(8)
21
+ # outfile.write_int 3
22
+ # outfile.write_int @vector_tag
23
+
24
+ @path_components.each{ |pc| pc.write(outfile); @file.seek(26, IO::SEEK_CUR) }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,53 @@
1
+ class PSD
2
+ class Layer
3
+ module Helpers
4
+ # Does this layer represent the start of a folder section?
5
+ def folder?
6
+ if info.has_key?(:section_divider)
7
+ info[:section_divider].is_folder
8
+ elsif info.has_key?(:nested_section_divider)
9
+ info[:nested_section_divider].is_folder
10
+ else
11
+ name == "<Layer group>"
12
+ end
13
+ end
14
+
15
+ # Does this layer represent the end of a folder section?
16
+ def folder_end?
17
+ if info.has_key?(:section_divider)
18
+ info[:section_divider].is_hidden
19
+ elsif info.has_key?(:nested_section_divider)
20
+ info[:nested_section_divider].is_hidden
21
+ else
22
+ name == "</Layer group>"
23
+ end
24
+ end
25
+
26
+ # Is this layer visible?
27
+ def visible?
28
+ @visible
29
+ end
30
+
31
+ # Is this layer hidden?
32
+ def hidden?
33
+ !@visible
34
+ end
35
+
36
+ # Helper that exports the text data in this layer, if any.
37
+ def text
38
+ return nil unless info[:type]
39
+ info[:type].to_hash
40
+ end
41
+
42
+ def layer_type
43
+ return 'normal' unless info.has_key?(:section_divider)
44
+ info[:section_divider].layer_type
45
+ end
46
+
47
+ def fill_opacity
48
+ return nil unless info.has_key?(:fill_opacity)
49
+ info[:fill_opacity].enabled
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,88 @@
1
+ class PSD
2
+ class Layer
3
+ module Info
4
+ # All of the extra layer info sections that we know how to parse.
5
+ LAYER_INFO = {
6
+ type: TypeTool,
7
+ legacy_type: LegacyTypeTool,
8
+ metadata: MetadataSetting,
9
+ layer_name_source: LayerNameSource,
10
+ object_effects: ObjectEffects,
11
+ name: UnicodeName,
12
+ section_divider: LayerSectionDivider,
13
+ nested_section_divider: NestedLayerDivider,
14
+ reference_point: ReferencePoint,
15
+ layer_id: LayerID,
16
+ fill_opacity: FillOpacity,
17
+ placed_layer: PlacedLayer,
18
+ vector_mask: VectorMask
19
+ }
20
+
21
+ attr_reader :adjustments
22
+ alias :info :adjustments
23
+
24
+ private
25
+
26
+ # This section is a bit tricky to parse because it represents all of the
27
+ # extra data that describes this layer.
28
+ def parse_layer_info
29
+ @extra_data_begin = @file.tell
30
+
31
+ while @file.tell < @layer_end
32
+ # Signature, don't need
33
+ @file.seek 4, IO::SEEK_CUR
34
+
35
+ # Key, very important
36
+ key = @file.read_string(4)
37
+ @info_keys << key
38
+
39
+ length = Util.pad2 @file.read_int
40
+ pos = @file.tell
41
+
42
+ info_parsed = false
43
+ LAYER_INFO.each do |name, info|
44
+ next unless info.key == key
45
+
46
+ PSD.logger.debug "Layer Info: key = #{key}, start = #{pos}, length = #{length}"
47
+
48
+ begin
49
+ i = info.new(@file, length)
50
+ i.parse
51
+
52
+ @adjustments[name] = i
53
+ info_parsed = true
54
+ rescue Exception => e
55
+ PSD.logger.error "Parsing error: key = #{key}, message = #{e.message}"
56
+ PSD.logger.error e.backtrace.join("\n")
57
+ end
58
+
59
+ break
60
+ end
61
+
62
+ if !info_parsed
63
+ PSD.logger.debug "Skipping: key = #{key}, pos = #{@file.tell}, length = #{length}"
64
+ @file.seek pos + length
65
+ end
66
+
67
+ if @file.tell != (pos + length)
68
+ PSD.logger.warn "Layer info key #{key} ended at #{@file.tell}, expected #{pos + length}"
69
+ @file.seek pos + length
70
+ end
71
+ end
72
+
73
+ @extra_data_end = @file.tell
74
+ end
75
+
76
+ def export_extra_data(outfile)
77
+ outfile.write @file.read(@extra_data_end - @extra_data_begin)
78
+ if @path_components && !@path_components.empty?
79
+ outfile.seek @vector_mask_begin
80
+ @file.seek @vector_mask_begin
81
+
82
+ write_vector_mask(outfile)
83
+ @file.seek outfile.tell
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,19 @@
1
+ class PSD
2
+ class Layer
3
+ module Mask
4
+ attr_reader :mask
5
+
6
+ private
7
+
8
+ def parse_mask_data
9
+ @mask_begin = @file.tell
10
+ @mask = PSD::Mask.read(@file)
11
+ @mask_end = @file.tell
12
+ end
13
+
14
+ def export_mask_data(outfile)
15
+ outfile.write @file.read(@mask_end - @mask_begin + 4)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ class PSD
2
+ class Layer
3
+ module Name
4
+ # Gets the name of this layer. If the PSD file is from an even remotely
5
+ # recent version of Photoshop, this data is stored as extra layer info and
6
+ # as a UTF-16 name. Otherwise, it's stored in a legacy block.
7
+ def name
8
+ if @adjustments.has_key?(:name)
9
+ return @adjustments[:name].data
10
+ end
11
+
12
+ return @legacy_name
13
+ end
14
+
15
+ private
16
+
17
+ # The old school layer names are encoded in MacRoman format,
18
+ # not UTF-8. Luckily Ruby kicks ass at character conversion.
19
+ def parse_legacy_layer_name
20
+ @legacy_name_start = @file.tell
21
+
22
+ len = Util.pad4 @file.read(1).bytes.to_a[0]
23
+ @legacy_name = @file.read_string(len)
24
+
25
+ @legacy_name_end = @file.tell
26
+ end
27
+
28
+ def export_legacy_layer_name(outfile)
29
+ outfile.write @file.read(@legacy_name_end - @legacy_name_start)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ class PSD
2
+ class Layer
3
+ module PathComponents
4
+ # Attempt to translate this layer and modify the document.
5
+ def translate(x=0, y=0)
6
+ @left += x
7
+ @right += x
8
+ @top += y
9
+ @bottom += y
10
+
11
+ @path_components.each{ |p| p.translate(x,y) } if @path_components
12
+ end
13
+
14
+ # Attempt to scale the path components within this layer.
15
+ def scale_path_components(xr, yr)
16
+ return unless @path_components
17
+
18
+ @path_components.each{ |p| p.scale(xr, yr) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ class PSD
2
+ class Layer
3
+ module PositionAndChannels
4
+ attr_reader :top, :left, :bottom, :right, :cols, :rows
5
+ attr_reader :channels, :channels_info
6
+
7
+ alias :width :cols
8
+ alias :height :rows
9
+
10
+ private
11
+
12
+ def parse_position_and_channels
13
+ start_section(:info)
14
+
15
+ @top = @file.read_int
16
+ @left = @file.read_int
17
+ @bottom = @file.read_int
18
+ @right = @file.read_int
19
+ @channels = @file.read_short
20
+
21
+ @rows = @bottom - @top
22
+ @cols = @right - @left
23
+
24
+ @channels.times do
25
+ channel_id = @file.read_short
26
+ channel_length = @file.read_int
27
+
28
+ @channels_info << {id: channel_id, length: channel_length}
29
+ end
30
+
31
+ end_section(:info)
32
+ end
33
+
34
+ def export_position_and_channels(outfile)
35
+ [@top, @left, @bottom, @right].each { |val| outfile.write_int(val) }
36
+ outfile.write_short(@channels)
37
+
38
+ @channels_info.each do |channel_info|
39
+ outfile.write_short channel_info[:id]
40
+ outfile.write_int channel_info[:length]
41
+ end
42
+
43
+ @file.seek end_of_section(:info)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ require_relative '../layer_info'
2
+
3
+ class PSD
4
+ # Not 100% sure what the purpose of this key is, but it seems to exist
5
+ # whenever the lsct key doesn't. Parsing this like a layer section
6
+ # divider seems to solve a lot of parsing issues with folders.
7
+ #
8
+ # See https://github.com/layervault/psd.rb/issues/38
9
+ class NestedLayerDivider < LayerInfo
10
+ @key = 'lsdk'
11
+
12
+ attr_reader :is_folder, :is_hidden
13
+
14
+ def initialize(file, length)
15
+ super
16
+
17
+ @is_folder = false
18
+ @is_hidden = false
19
+ end
20
+
21
+ def parse
22
+ code = @file.read_int
23
+
24
+ case code
25
+ when 1, 2 then @is_folder = true
26
+ when 3 then @is_hidden = true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,7 +4,7 @@ class PSD
4
4
  class LayerSectionDivider < LayerInfo
5
5
  @key = 'lsct'
6
6
 
7
- attr_reader :layer_type, :is_folder, :is_hidden
7
+ attr_reader :layer_type, :is_folder, :is_hidden, :blend_mode, :sub_type
8
8
 
9
9
  SECTION_DIVIDER_TYPES = [
10
10
  "other",
@@ -18,6 +18,8 @@ class PSD
18
18
 
19
19
  @is_folder = false
20
20
  @is_hidden = false
21
+ @blend_mode = nil
22
+ @sub_type = nil
21
23
  end
22
24
 
23
25
  def parse
@@ -29,6 +31,17 @@ class PSD
29
31
  when 3 then @is_hidden = true
30
32
  end
31
33
 
34
+ PSD.logger.warn "Section divider is unexpected value: #{code}" if code > 4
35
+
36
+ return self unless @length >= 12
37
+
38
+ @file.seek 4, IO::SEEK_CUR # sig
39
+ @blend_mode = @file.read_string(4)
40
+
41
+ return self unless @length >= 16
42
+
43
+ @sub_type = @file.read_int == 0 ? 'normal' : 'scene group'
44
+
32
45
  return self
33
46
  end
34
47
  end
@@ -56,7 +56,7 @@ class PSD
56
56
 
57
57
  layers.each do |layer|
58
58
  @file.seek 8, IO::SEEK_CUR and next if layer.folder? || layer.folder_end?
59
- layer.parse_channel_image!(@header, @options[:parse_layer_images])
59
+ layer.parse_channel_image(@header, @options[:parse_layer_images])
60
60
  end
61
61
  end
62
62
 
@@ -1,3 +1,3 @@
1
1
  class PSD
2
- VERSION = "1.3.2"
2
+ VERSION = "1.3.3"
3
3
  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.3.2
4
+ version: 1.3.3
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-10-16 00:00:00.000000000 Z
12
+ date: 2013-10-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -159,8 +159,19 @@ files:
159
159
  - lib/psd/image_modes/greyscale.rb
160
160
  - lib/psd/image_modes/rgb.rb
161
161
  - lib/psd/layer.rb
162
+ - lib/psd/layer/blend_modes.rb
163
+ - lib/psd/layer/blending_ranges.rb
164
+ - lib/psd/layer/channel_image.rb
165
+ - lib/psd/layer/exporting.rb
166
+ - lib/psd/layer/helpers.rb
167
+ - lib/psd/layer/info.rb
168
+ - lib/psd/layer/mask.rb
169
+ - lib/psd/layer/name.rb
170
+ - lib/psd/layer/path_components.rb
171
+ - lib/psd/layer/position_and_channels.rb
162
172
  - lib/psd/layer_info.rb
163
173
  - lib/psd/layer_info/fill_opacity.rb
174
+ - lib/psd/layer_info/layer_group.rb
164
175
  - lib/psd/layer_info/layer_id.rb
165
176
  - lib/psd/layer_info/layer_name_source.rb
166
177
  - lib/psd/layer_info/layer_section_divider.rb