psd 1.4.3 → 1.4.4

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: e989ef2453ff6c7b5e1c5e78590921ba53c679d6
4
- data.tar.gz: 7b4dadc4460ad50512119776d4ca3e97f30782a9
3
+ metadata.gz: cfdf8466e7c4a858bf93d47ec823c26ef3a667fa
4
+ data.tar.gz: bab2a4414c765c27a0d8de12b401067a2a894ba5
5
5
  SHA512:
6
- metadata.gz: 219a65e3412907b5672831101ad86bc7ba900dee53f571261cae77a39137589028c170950f52b85e477b039aa596840abb35a6ce0506d515e7866162390eec56
7
- data.tar.gz: 5b8436682d202ceaa47e75dccdd59470f868231473cb91c8212aefb35ed2024dfa709adddd8b650b17d8411561dfb12dadb1afeb63445aefecc64fdaea1b9780
6
+ metadata.gz: c80d095fbce20218530a13a3adcf29d9193a6dfe4e4a7b73bc3202bc04ddc3d1a80869d2c9f648ebca606f460647cc34e9d95f81a8bcdba0a044bd2070be8da0
7
+ data.tar.gz: fabf5fcfceeab8e772228ac3088aa73c5d9382cae2d31f41b405cfd5c2542139f08bbeb1f5e20fe3f99a153b8e65071398315f7fb47b36bd2070e86ec4a0e065
data/README.md CHANGED
@@ -217,11 +217,7 @@ PSD.debug = true
217
217
 
218
218
  You can build previews of any subset or version of the PSD document. This is useful for generating previews of layer comps or exporting individual layer groups as images.
219
219
 
220
- **NOTE: You MUST initialize with the parse_layer_images option set to true, as shown below.**
221
-
222
220
  ``` ruby
223
- psd = PSD.new(file, parse_layer_images: true)
224
-
225
221
  # Save a layer comp
226
222
  psd.tree.filter_by_comp("Version A").save_as_png('./Version A.png')
227
223
 
@@ -235,3 +231,4 @@ There are a few features that are currently missing from PSD.rb.
235
231
 
236
232
  * More image modes + depths for image exporting
237
233
  * A few layer info blocks
234
+ * Support for rendering all layer styles
data/lib/psd.rb CHANGED
@@ -24,11 +24,6 @@ class PSD
24
24
  include Helpers
25
25
  include NodeExporting
26
26
 
27
- DEFAULTS = {
28
- parse_image: false,
29
- parse_layer_images: false
30
- }
31
-
32
27
  attr_reader :file, :opts
33
28
  alias :options :opts
34
29
 
@@ -55,7 +50,7 @@ class PSD
55
50
  @file = PSD::File.new(file, 'rb')
56
51
  @file.seek 0 # If the file was previously used and not closed
57
52
 
58
- @opts = DEFAULTS.merge(opts)
53
+ @opts = opts
59
54
  @header = nil
60
55
  @resources = nil
61
56
  @layer_mask = nil
@@ -74,7 +69,7 @@ class PSD
74
69
  header
75
70
  resources
76
71
  layer_mask
77
- image if @opts[:parse_image]
72
+ image
78
73
 
79
74
  @parsed = true
80
75
 
@@ -121,7 +116,12 @@ class PSD
121
116
  ensure_resources
122
117
  ensure_layer_mask
123
118
 
124
- @image ||= Image.new(@file, @header).parse
119
+ @image ||= (
120
+ # The image is the last section in the file, so we don't have to
121
+ # bother with skipping over the bytes to read more data.
122
+ image = Image.new(@file, @header)
123
+ LazyExecute.new(image, @file).later(:parse)
124
+ )
125
125
  end
126
126
 
127
127
  # Export the current file to a new PSD. This may or may not work.
@@ -9,7 +9,6 @@ class PSD
9
9
 
10
10
  attr_reader :width, :height, :mask_data
11
11
 
12
-
13
12
  def initialize(file, header, layer)
14
13
  @layer = layer
15
14
 
@@ -27,7 +26,7 @@ class PSD
27
26
  def skip
28
27
  PSD.logger.debug "Skipping channel image data. Layer = #{@layer.name}"
29
28
  @channels_info.each do |ch|
30
- @file.seek ch.length, IO::SEEK_CUR
29
+ @file.seek ch[:length], IO::SEEK_CUR
31
30
  end
32
31
  end
33
32
 
@@ -66,7 +65,7 @@ class PSD
66
65
 
67
66
  if finish != start + ch_info[:length]
68
67
  PSD.logger.error "Read incorrect number of bytes for channel ##{ch_info[:id]}. Expected = #{ch_info[:length]}, Actual = #{finish - start}"
69
- @file.seek start + @ch_info[:length]
68
+ @file.seek start + ch_info[:length]
70
69
  end
71
70
  end
72
71
 
@@ -3,9 +3,9 @@ class PSD
3
3
  module ChannelImage
4
4
  attr_reader :image
5
5
 
6
- def parse_channel_image(header, parse)
7
- @image = PSD::ChannelImage.new(@file, header, self)
8
- parse ? @image.parse : @image.skip
6
+ def parse_channel_image(header)
7
+ image = PSD::ChannelImage.new(@file, header, self)
8
+ @image = LazyExecute.new(image, @file).now(:skip).later(:parse)
9
9
  end
10
10
  end
11
11
  end
@@ -44,34 +44,20 @@ class PSD
44
44
  length = Util.pad2 @file.read_int
45
45
  pos = @file.tell
46
46
 
47
- info_parsed = false
47
+ key_parseable = false
48
48
  LAYER_INFO.each do |name, info|
49
49
  next unless info.key == key
50
50
 
51
51
  PSD.logger.debug "Layer Info: key = #{key}, start = #{pos}, length = #{length}"
52
52
 
53
- begin
54
- i = info.new(self, length)
55
- i.parse
56
-
57
- @adjustments[name] = i
58
- info_parsed = true
59
- rescue Exception => e
60
- PSD.logger.error "Parsing error: key = #{key}, message = #{e.message}"
61
- PSD.logger.error e.backtrace.join("\n")
62
- end
63
-
64
- break
65
- end
66
-
67
- if !info_parsed
68
- PSD.logger.debug "Skipping: layer = #{name}, key = #{key}, pos = #{@file.tell}, length = #{length}"
69
- @file.seek pos + length
53
+ i = info.new(self, length)
54
+ @adjustments[name] = LazyExecute.new(i, @file).now(:skip).later(:parse)
55
+ key_parseable = true and break
70
56
  end
71
57
 
72
- if @file.tell != (pos + length)
73
- PSD.logger.warn "Layer info key #{key} ended at #{@file.tell}, expected #{pos + length}"
74
- @file.seek pos + length
58
+ unless key_parseable
59
+ PSD.logger.debug "Skipping unknown layer info block: key = #{key}, length = #{length}"
60
+ @file.seek length, IO::SEEK_CUR
75
61
  end
76
62
  end
77
63
 
@@ -81,17 +67,6 @@ class PSD
81
67
  def vector_mask
82
68
  info[:vector_mask_2] || info[:vector_mask]
83
69
  end
84
-
85
- def export_extra_data(outfile)
86
- outfile.write @file.read(@extra_data_end - @extra_data_begin)
87
- if @path_components && !@path_components.empty?
88
- outfile.seek @vector_mask_begin
89
- @file.seek @vector_mask_begin
90
-
91
- write_vector_mask(outfile)
92
- @file.seek outfile.tell
93
- end
94
- end
95
70
  end
96
71
  end
97
72
  end
@@ -15,9 +15,13 @@ class PSD
15
15
  @data = {}
16
16
  end
17
17
 
18
+ def skip
19
+ @file.seek @section_end
20
+ end
21
+
18
22
  # Override this - default seeks to end of section
19
23
  def parse
20
- @file.seek @section_end
24
+ skip
21
25
  end
22
26
  end
23
27
  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)
60
60
  end
61
61
  end
62
62
 
@@ -107,11 +107,12 @@ class PSD
107
107
 
108
108
  def parse_global_mask
109
109
  length = @file.read_int
110
+
111
+ PSD.logger.debug "Global Mask: length = #{length}"
110
112
  return if length <= 0
111
113
 
112
114
  mask_end = @file.tell + length
113
- PSD.logger.debug "Global Mask: length = #{length}"
114
-
115
+
115
116
  @global_mask = {}
116
117
  @global_mask[:overlay_color_space] = @file.read_short
117
118
  @global_mask[:color_components] = 4.times.map { |i| @file.read_short >> 8 }
@@ -0,0 +1,54 @@
1
+ class PSD
2
+ # Used for lazily executing methods on demand
3
+ class LazyExecute
4
+ def initialize(obj, file)
5
+ @obj = obj
6
+ @file = file
7
+
8
+ @start_pos = @file.tell
9
+ @loaded = false
10
+ @load_method = nil
11
+ @load_args = []
12
+
13
+ PSD.logger.debug "Marked #{@obj.class.name} at position #{@start_pos} for lazy execution"
14
+ end
15
+
16
+ def now(method, *args, &block)
17
+ PSD.logger.debug "Executing #{@obj.class.name}##{method} now"
18
+ @obj.send(method, *args, &block)
19
+ return self
20
+ end
21
+
22
+ def later(method, *args)
23
+ @load_method = method
24
+ @load_args = args
25
+ return self
26
+ end
27
+
28
+ def loaded?
29
+ @loaded
30
+ end
31
+
32
+ def method_missing(method, *args, &block)
33
+ load! unless loaded?
34
+ @obj.send(method, *args, &block)
35
+ end
36
+
37
+ def inspect
38
+ "<PSD::LazyExecute @obj=#{@obj.class.name}, @pos=#{@start_pos}, @load_method=:#{@load_method}>"
39
+ end
40
+
41
+ private
42
+
43
+ def load!
44
+ PSD.logger.debug "Lazily executing #{@obj.class.name}##{@load_method}"
45
+ orig_pos = @file.tell
46
+ @file.seek @start_pos
47
+
48
+ @obj.send(@load_method, *@load_args)
49
+
50
+ @file.seek orig_pos
51
+ @loaded = true
52
+ end
53
+ end
54
+ end
data/lib/psd/logger.rb CHANGED
@@ -13,7 +13,7 @@ class PSD
13
13
  return @logger if @logger
14
14
 
15
15
  if debug || ENV['PSD_DEBUG']
16
- @logger = ::Logger.new(debug_output)
16
+ @logger = ::Logger.new(STDOUT)
17
17
  @logger.formatter = proc do |severity, datetime, progname, msg|
18
18
  "#{severity}: #{msg}\n"
19
19
  end
@@ -23,14 +23,6 @@ class PSD
23
23
 
24
24
  return @logger
25
25
  end
26
-
27
- def debug_output
28
- if ENV['PSD_DEBUG']
29
- ENV['PSD_DEBUG'] == 'STDOUT' ? STDOUT : ENV['PSD_DEBUG']
30
- end
31
-
32
- STDOUT
33
- end
34
26
  end
35
27
  end
36
28
 
@@ -48,8 +48,8 @@ class PSD
48
48
  PSD.logger.warn("Blend mode #{blending_mode} is not implemented") unless Compose.respond_to?(blending_mode)
49
49
  PSD.logger.debug("Blending #{layer.name} with #{blending_mode} blend mode")
50
50
 
51
- LayerStyles.new(layer, other).apply!
52
51
  other = ClippingMask.new(layer, other).apply
52
+ LayerStyles.new(layer, other).apply!
53
53
 
54
54
  blend_pixels!(blending_mode, layer, base, other, offset_x, offset_y)
55
55
  end
data/lib/psd/util.rb CHANGED
@@ -1,16 +1,17 @@
1
1
  class PSD
2
- class Util
3
- # Ensures value is a multiple of 2
4
- def self.pad2(i)
5
- ((i + 1) / 2) * 2
2
+ module Util
3
+ extend self
4
+
5
+ def pad2(i)
6
+ (i + 1) & ~0x01
6
7
  end
7
8
 
8
9
  # Ensures value is a multiple of 4
9
- def self.pad4(i)
10
- i - (i.modulo(4)) + 3
10
+ def pad4(i)
11
+ ((i + 4) & ~0x03) - 1
11
12
  end
12
13
 
13
- def self.clamp(num, min, max)
14
+ def clamp(num, min, max)
14
15
  [min, num, max].sort[1]
15
16
  end
16
17
  end
data/lib/psd/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class PSD
2
- VERSION = "1.4.3"
2
+ VERSION = "1.4.4"
3
3
  end
data/spec/image_spec.rb CHANGED
@@ -45,7 +45,6 @@ describe 'Image Exporting' do
45
45
  @psd.parse!
46
46
 
47
47
  image = @psd.tree.children.first.image
48
- expect(image).to be_an_instance_of(PSD::ChannelImage)
49
48
  expect(image.width).to eq(1)
50
49
  expect(image.height).to eq(1)
51
50
 
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe PSD::LazyExecute do
4
+ before(:each) do
5
+ @psd = PSD.new('spec/files/pixel.psd')
6
+ end
7
+
8
+ it 'initializes correctly' do
9
+ expect(@psd.image).to be_an_instance_of(PSD::LazyExecute)
10
+ expect(@psd.image).to_not be_loaded
11
+ expect(@psd.image.instance_variable_get(:@obj)).to be_an_instance_of(PSD::Image)
12
+ expect(@psd.image.instance_variable_get(:@start_pos)).to eq @psd.file.tell
13
+ expect(@psd.image.instance_variable_get(:@load_method)).to eq :parse
14
+ end
15
+
16
+ it 'loads when accessed the first time' do
17
+ @psd.image.to_png
18
+ expect(@psd.image).to be_loaded
19
+ end
20
+ end
data/spec/parsing_spec.rb CHANGED
@@ -74,12 +74,14 @@ describe 'Parsing' do
74
74
  expect(@psd.layer_mask.layers.size).to be > 0
75
75
  end
76
76
 
77
- it "should contain the global layer mask data" do
78
- expect(@psd.layer_mask.global_mask).to_not be_nil
79
- expect(@psd.layer_mask.global_mask).to include :overlay_color_space
80
- expect(@psd.layer_mask.global_mask).to include :color_components
81
- expect(@psd.layer_mask.global_mask).to include opacity: 1.0
82
- end
77
+ # The test file is actually missing the global mask. Need a new test file
78
+ # with it present.
79
+ # it "should contain the global layer mask data" do
80
+ # expect(@psd.layer_mask.global_mask).to_not be_nil
81
+ # expect(@psd.layer_mask.global_mask).to include :overlay_color_space
82
+ # expect(@psd.layer_mask.global_mask).to include :color_components
83
+ # expect(@psd.layer_mask.global_mask).to include opacity: 1.0
84
+ # end
83
85
  end
84
86
 
85
87
  describe 'Layers' do
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.4.3
4
+ version: 1.4.4
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-11-21 00:00:00.000000000 Z
12
+ date: 2013-11-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -192,6 +192,7 @@ files:
192
192
  - lib/psd/layer_info/vector_stroke_content.rb
193
193
  - lib/psd/layer_mask.rb
194
194
  - lib/psd/layer_styles.rb
195
+ - lib/psd/lazy_execute.rb
195
196
  - lib/psd/logger.rb
196
197
  - lib/psd/mask.rb
197
198
  - lib/psd/node.rb
@@ -223,8 +224,8 @@ files:
223
224
  - spec/files/slices.psd
224
225
  - spec/files/text.psd
225
226
  - spec/hierarchy_spec.rb
226
- - spec/identity_spec.rb
227
227
  - spec/image_spec.rb
228
+ - spec/lazy_execute_spec.rb
228
229
  - spec/parsing_spec.rb
229
230
  - spec/psd_spec.rb
230
231
  - spec/slices_spec.rb
@@ -250,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
251
  version: '0'
251
252
  requirements: []
252
253
  rubyforge_project:
253
- rubygems_version: 2.0.8
254
+ rubygems_version: 2.1.11
254
255
  signing_key:
255
256
  specification_version: 4
256
257
  summary: General purpose library for parsing Photoshop files
@@ -263,8 +264,8 @@ test_files:
263
264
  - spec/files/slices.psd
264
265
  - spec/files/text.psd
265
266
  - spec/hierarchy_spec.rb
266
- - spec/identity_spec.rb
267
267
  - spec/image_spec.rb
268
+ - spec/lazy_execute_spec.rb
268
269
  - spec/parsing_spec.rb
269
270
  - spec/psd_spec.rb
270
271
  - spec/slices_spec.rb
@@ -1,34 +0,0 @@
1
- require 'spec_helper'
2
- require 'tempfile'
3
-
4
- describe "Identity exports" do
5
- it "should export the simplest PSD" do
6
- filepath = 'spec/files/simplest.psd'
7
- psd = PSD.new(filepath)
8
- psd.parse!
9
- tmpfile = Tempfile.new("simplest_export.psd")
10
- psd.export tmpfile.path
11
-
12
- expect(Digest::MD5.hexdigest(tmpfile.read)).to eq(Digest::MD5.hexdigest(File.read(filepath)))
13
- end
14
-
15
- it "should export a file with a layer" do
16
- filepath = 'spec/files/one_layer.psd'
17
- psd = PSD.new(filepath)
18
- psd.parse!
19
- tmpfile = Tempfile.new("one_layer_export.psd")
20
- psd.export tmpfile.path
21
-
22
- expect(Digest::MD5.hexdigest(tmpfile.read)).to eq(Digest::MD5.hexdigest(File.read(filepath)))
23
- end
24
-
25
- it "should export a PSD with vector paths" do
26
- filepath = 'spec/files/path.psd'
27
- psd = PSD.new(filepath)
28
- psd.parse!
29
- tmpfile = Tempfile.new("path_export.psd")
30
- psd.export tmpfile.path
31
-
32
- expect(Digest::MD5.hexdigest(tmpfile.read)).to eq(Digest::MD5.hexdigest(File.read(filepath)))
33
- end
34
- end