psd 2.1.2 → 3.1.2

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/psd.rb +8 -6
  4. data/lib/psd/blend_mode.rb +46 -38
  5. data/lib/psd/channel_image.rb +9 -5
  6. data/lib/psd/descriptor.rb +39 -16
  7. data/lib/psd/header.rb +33 -32
  8. data/lib/psd/image_formats/rle.rb +4 -10
  9. data/lib/psd/image_modes/rgb.rb +4 -4
  10. data/lib/psd/layer.rb +1 -15
  11. data/lib/psd/layer/blend_modes.rb +12 -12
  12. data/lib/psd/layer/helpers.rb +8 -10
  13. data/lib/psd/layer/info.rb +9 -7
  14. data/lib/psd/layer/position_and_channels.rb +0 -4
  15. data/lib/psd/layer_info.rb +0 -4
  16. data/lib/psd/layer_info/blend_clipping_elements.rb +4 -2
  17. data/lib/psd/layer_info/blend_interior_elements.rb +4 -2
  18. data/lib/psd/layer_info/fill_opacity.rb +4 -2
  19. data/lib/psd/layer_info/layer_group.rb +4 -2
  20. data/lib/psd/layer_info/layer_id.rb +4 -2
  21. data/lib/psd/layer_info/layer_name_source.rb +4 -2
  22. data/lib/psd/layer_info/layer_section_divider.rb +4 -2
  23. data/lib/psd/layer_info/legacy_typetool.rb +5 -3
  24. data/lib/psd/layer_info/locked.rb +4 -2
  25. data/lib/psd/layer_info/metadata_setting.rb +4 -2
  26. data/lib/psd/layer_info/object_effects.rb +4 -2
  27. data/lib/psd/layer_info/pattern.rb +14 -0
  28. data/lib/psd/layer_info/placed_layer.rb +4 -2
  29. data/lib/psd/layer_info/reference_point.rb +4 -2
  30. data/lib/psd/layer_info/sheet_color.rb +18 -0
  31. data/lib/psd/layer_info/solid_color.rb +36 -0
  32. data/lib/psd/layer_info/typetool.rb +4 -2
  33. data/lib/psd/layer_info/unicode_name.rb +4 -2
  34. data/lib/psd/layer_info/vector_mask.rb +4 -2
  35. data/lib/psd/layer_info/vector_origination.rb +14 -0
  36. data/lib/psd/layer_info/vector_stroke.rb +4 -2
  37. data/lib/psd/layer_info/vector_stroke_content.rb +4 -2
  38. data/lib/psd/layer_mask.rb +2 -8
  39. data/lib/psd/lazy_execute.rb +5 -1
  40. data/lib/psd/node.rb +112 -48
  41. data/lib/psd/nodes/ancestry.rb +80 -75
  42. data/lib/psd/nodes/build_preview.rb +4 -4
  43. data/lib/psd/nodes/group.rb +35 -0
  44. data/lib/psd/nodes/layer.rb +40 -0
  45. data/lib/psd/nodes/root.rb +90 -0
  46. data/lib/psd/nodes/search.rb +19 -19
  47. data/lib/psd/path_record.rb +1 -71
  48. data/lib/psd/renderer.rb +6 -5
  49. data/lib/psd/renderer/blender.rb +10 -5
  50. data/lib/psd/renderer/cairo_helpers.rb +46 -0
  51. data/lib/psd/renderer/canvas.rb +39 -19
  52. data/lib/psd/renderer/canvas_management.rb +2 -2
  53. data/lib/psd/renderer/clipping_mask.rb +5 -4
  54. data/lib/psd/renderer/compose.rb +61 -68
  55. data/lib/psd/renderer/layer_styles.rb +15 -5
  56. data/lib/psd/renderer/layer_styles/color_overlay.rb +46 -27
  57. data/lib/psd/renderer/mask.rb +26 -22
  58. data/lib/psd/renderer/mask_canvas.rb +12 -0
  59. data/lib/psd/renderer/vector_shape.rb +239 -0
  60. data/lib/psd/resource_section.rb +4 -7
  61. data/lib/psd/resources.rb +4 -19
  62. data/lib/psd/resources/base.rb +27 -0
  63. data/lib/psd/resources/guides.rb +6 -4
  64. data/lib/psd/resources/layer_comps.rb +6 -4
  65. data/lib/psd/resources/slices.rb +7 -5
  66. data/lib/psd/version.rb +1 -1
  67. data/psd.gemspec +1 -2
  68. data/spec/files/blendmodes.psd +0 -0
  69. data/spec/hierarchy_spec.rb +5 -0
  70. metadata +27 -26
  71. data/lib/psd/layer_info/vector_mask_2.rb +0 -10
  72. data/lib/psd/node_exporting.rb +0 -20
  73. data/lib/psd/node_group.rb +0 -86
  74. data/lib/psd/node_layer.rb +0 -81
  75. data/lib/psd/node_root.rb +0 -93
  76. data/lib/psd/nodes/has_children.rb +0 -13
  77. data/lib/psd/nodes/lock_to_origin.rb +0 -7
  78. data/lib/psd/nodes/parse_layers.rb +0 -18
  79. data/lib/psd/renderer/layer_styles/drop_shadow.rb +0 -75
  80. data/lib/psd/section.rb +0 -26
@@ -1,8 +1,10 @@
1
- require_relative '../layer_info'
1
+ require 'psd/layer_info'
2
2
 
3
3
  class PSD
4
4
  class VectorMask < LayerInfo
5
- @key = 'vmsk'
5
+ def self.should_parse?(key)
6
+ ['vmsk', 'vsms'].include?(key)
7
+ end
6
8
 
7
9
  attr_reader :invert, :not_link, :disable, :paths
8
10
 
@@ -0,0 +1,14 @@
1
+ require 'psd/layer_info'
2
+
3
+ class PSD
4
+ class VectorOrigination < LayerInfo
5
+ def self.should_parse?(key)
6
+ key == 'vogk'
7
+ end
8
+
9
+ def parse
10
+ @file.seek 8, IO::SEEK_CUR
11
+ @data = Descriptor.new(@file).parse
12
+ end
13
+ end
14
+ end
@@ -1,8 +1,10 @@
1
- require_relative '../layer_info'
1
+ require 'psd/layer_info'
2
2
 
3
3
  class PSD
4
4
  class VectorStroke < LayerInfo
5
- @key = 'vstk'
5
+ def self.should_parse?(key)
6
+ key == 'vstk'
7
+ end
6
8
 
7
9
  def parse
8
10
  version = @file.read_int
@@ -1,8 +1,10 @@
1
- require_relative '../layer_info'
1
+ require 'psd/layer_info'
2
2
 
3
3
  class PSD
4
4
  class VectorStrokeContent < LayerInfo
5
- @key = 'vscg'
5
+ def self.should_parse?(key)
6
+ key == 'vscg'
7
+ end
6
8
 
7
9
  attr_reader :key
8
10
 
@@ -2,8 +2,6 @@ class PSD
2
2
  # Covers parsing the global mask and controls parsing of all the
3
3
  # layers/folders in the document.
4
4
  class LayerMask
5
- include Section
6
-
7
5
  attr_reader :layers, :global_mask
8
6
 
9
7
  # Store a reference to the file and the header and initialize the defaults.
@@ -25,11 +23,8 @@ class PSD
25
23
  return self
26
24
  end
27
25
 
28
- # Parse this section, including all of the layers and folders. Once implemented, this
29
- # will also trigger parsing of the channel images for each layer.
26
+ # Parse this section, including all of the layers and folders.
30
27
  def parse
31
- start_section
32
-
33
28
  mask_size = @file.read_int
34
29
  finish = @file.tell + mask_size
35
30
 
@@ -46,7 +41,7 @@ class PSD
46
41
  end
47
42
 
48
43
  if layer_count * (18 + 6 * @header.channels) > layer_info_size
49
- raise "Unlikely number of layers parsed: #{layer_count}"
44
+ PSD.logger.error "Unlikely number of layers parsed: #{layer_count}"
50
45
  end
51
46
 
52
47
  @layer_section_start = @file.tell
@@ -67,7 +62,6 @@ class PSD
67
62
 
68
63
  # Ensure we're at the end of this section
69
64
  @file.seek finish
70
- end_section
71
65
 
72
66
  return self
73
67
  end
@@ -41,7 +41,11 @@ class PSD
41
41
  end
42
42
 
43
43
  def inspect
44
- "<PSD::LazyExecute @obj=#{@obj.class.name}, @pos=#{@start_pos}, @load_method=:#{@load_method}>"
44
+ if loaded?
45
+ @obj.inspect
46
+ else
47
+ "<PSD::LazyExecute @obj=#{@obj.class.name}, @pos=#{@start_pos}, @load_method=:#{@load_method}>"
48
+ end
45
49
  end
46
50
 
47
51
  private
data/lib/psd/node.rb CHANGED
@@ -1,70 +1,134 @@
1
- require_relative 'nodes/ancestry'
2
- require_relative 'nodes/search'
1
+ require 'psd/nodes/ancestry'
2
+ require 'psd/nodes/search'
3
+ require 'psd/nodes/build_preview'
3
4
 
4
5
  # Internal structure to help us build trees of a Photoshop documents.
5
6
  # A lot of method names borrowed from the Ruby ancestry gem.
6
7
  class PSD
7
- class Node
8
- include ParseLayers
9
- include Ancestry
10
- include Search
11
- include BuildPreview
8
+ module Node
9
+ class Base
10
+ include Enumerable
11
+ include Ancestry
12
+ include Search
13
+ include BuildPreview
12
14
 
13
- # Default properties that all nodes contain
14
- PROPERTIES = [:name, :left, :right, :top, :bottom, :height, :width]
15
+ # Default properties that all nodes contain
16
+ PROPERTIES = [:name, :left, :right, :top, :bottom, :height, :width]
15
17
 
16
- attr_accessor :parent, :children, :layer, :force_visible, :top, :left
18
+ attr_reader :name, :parent
19
+ attr_accessor :children, :layer, :force_visible, :top_offset, :left_offset
17
20
 
18
- def initialize(layers=[])
19
- parse_layers(layers)
21
+ delegate :psd, to: :parent
22
+ delegate :name, to: :layer
23
+ delegate :each, to: :children
24
+ delegate :document_dimensions, to: :parent
20
25
 
21
- @force_visible = nil
22
- @top = @layer.top
23
- @left = @layer.left
24
- end
26
+ def initialize(layer, parent = nil)
27
+ @layer = layer
28
+ @layer.node = self
25
29
 
26
- def hidden?
27
- !visible?
28
- end
30
+ @parent = parent
31
+ @children = []
32
+
33
+ @force_visible = nil
34
+ @top = @layer.top.to_i
35
+ @bottom = @layer.bottom.to_i
36
+ @left = @layer.left.to_i
37
+ @right = @layer.right.to_i
29
38
 
30
- def visible?
31
- return false if @layer.clipped? && !next_sibling.visible?
32
- force_visible.nil? ? @layer.visible? : force_visible
33
- end
39
+ @top_offset = 0
40
+ @left_offset = 0
41
+ end
34
42
 
35
- def psd
36
- parent.psd
37
- end
43
+ def top
44
+ @top + @top_offset
45
+ end
38
46
 
39
- def layer?
40
- is_a?(PSD::Node::Layer)
41
- end
47
+ def bottom
48
+ @bottom + @top_offset
49
+ end
42
50
 
43
- def group?
44
- is_a?(PSD::Node::Group) || is_a?(PSD::Node::Root)
45
- end
51
+ def left
52
+ @left + @left_offset
53
+ end
46
54
 
47
- def debug_name
48
- root? ? ":root:" : name
49
- end
55
+ def right
56
+ @right + @left_offset
57
+ end
50
58
 
51
- def to_hash
52
- hash = {
53
- type: nil,
54
- visible: visible?,
55
- opacity: @layer.opacity / 255.0,
56
- blending_mode: @layer.blending_mode
57
- }
59
+ def width
60
+ right - left
61
+ end
58
62
 
59
- PROPERTIES.each do |p|
60
- hash[p] = self.send(p)
63
+ def height
64
+ bottom - top
61
65
  end
62
66
 
63
- hash
64
- end
67
+ def hidden?
68
+ !visible?
69
+ end
70
+
71
+ def visible?
72
+ return false if @layer.clipped? && !clipping_mask.visible?
73
+ @force_visible.nil? ? @layer.visible? : @force_visible
74
+ end
75
+
76
+ def clipping_mask
77
+ return nil unless @layer.clipped?
65
78
 
66
- def document_dimensions
67
- @parent.document_dimensions
79
+ @clipping_mask ||= (
80
+ mask_node = next_sibling
81
+ while mask_node.clipped?
82
+ mask_node = mask_node.next_sibling
83
+ end
84
+
85
+ mask_node
86
+ )
87
+ end
88
+ alias_method :clipped_by, :clipping_mask
89
+
90
+ def layer?
91
+ is_a?(PSD::Node::Layer)
92
+ end
93
+
94
+ def group?(include_root = true)
95
+ is_a?(PSD::Node::Group) || (include_root && is_a?(PSD::Node::Root))
96
+ end
97
+
98
+ def debug_name
99
+ root? ? ":root:" : name
100
+ end
101
+
102
+ def to_hash
103
+ hash = {
104
+ type: nil,
105
+ visible: visible?,
106
+ opacity: @layer.opacity / 255.0,
107
+ blending_mode: @layer.blending_mode
108
+ }
109
+
110
+ PROPERTIES.each do |p|
111
+ hash[p] = self.send(p)
112
+ end
113
+
114
+ hash
115
+ end
116
+
117
+ protected
118
+
119
+ def update_dimensions!
120
+ return if layer?
121
+
122
+ children.each { |child| child.update_dimensions! }
123
+
124
+ return if root?
125
+
126
+ non_empty_children = children.reject(&:empty?)
127
+ @left = non_empty_children.map(&:left).min || 0
128
+ @top = non_empty_children.map(&:top).min || 0
129
+ @bottom = non_empty_children.map(&:bottom).max || 0
130
+ @right = non_empty_children.map(&:right).max || 0
131
+ end
68
132
  end
69
133
  end
70
134
  end
@@ -1,98 +1,103 @@
1
1
  class PSD
2
- class Node
2
+ module Node
3
3
  # Collection of methods to help in traversing the PSD tree structure.
4
4
  module Ancestry
5
- # Returns the root node
6
- def root
7
- return self if is_root?
8
- return parent.root
9
- end
5
+ extend ActiveSupport::Concern
10
6
 
11
- # Is this node the root node?
12
- def root?
13
- self.is_a?(PSD::Node::Root)
14
- end
15
- alias :is_root? :root?
7
+ included do
8
+ # Returns the root node
9
+ def root
10
+ return self if is_root?
11
+ return parent.root
12
+ end
16
13
 
17
- # Returns all ancestors in the path of this node. This
18
- # does NOT return the root node.
19
- def ancestors
20
- return [] if parent.nil? || parent.is_root?
21
- return parent.ancestors + [parent]
22
- end
14
+ # Is this node the root node?
15
+ def root?
16
+ self.is_a?(PSD::Node::Root)
17
+ end
18
+ alias :is_root? :root?
23
19
 
24
- # Does this node have any children nodes?
25
- def has_children?
26
- children.length > 0
27
- end
20
+ # Returns all ancestors in the path of this node. This
21
+ # does NOT return the root node.
22
+ def ancestors
23
+ return [] if parent.nil? || parent.is_root?
24
+ return parent.ancestors + [parent]
25
+ end
28
26
 
29
- # Inverse of has_children?
30
- def childless?
31
- !has_children?
32
- end
27
+ # Does this node have any children nodes?
28
+ def has_children?
29
+ children.length > 0
30
+ end
33
31
 
34
- # Returns all sibling nodes including the current node. Can also
35
- # be thought of as all children of the parent of this node.
36
- def siblings
37
- return [] if parent.nil?
38
- parent.children
39
- end
32
+ # Inverse of has_children?
33
+ def childless?
34
+ !has_children?
35
+ end
40
36
 
41
- def next_sibling
42
- return nil if parent.nil?
43
- index = siblings.index(self)
44
- siblings[index + 1]
45
- end
37
+ # Returns all sibling nodes including the current node. Can also
38
+ # be thought of as all children of the parent of this node.
39
+ def siblings
40
+ return [] if parent.nil?
41
+ parent.children
42
+ end
46
43
 
47
- def prev_sibling
48
- return nil if parent.nil?
49
- index = siblings.index(self)
50
- siblings[index - 1]
51
- end
44
+ def next_sibling
45
+ return nil if parent.nil?
46
+ index = siblings.index(self)
47
+ siblings[index + 1]
48
+ end
52
49
 
53
- # Does this node have any siblings?
54
- def has_siblings?
55
- siblings.length > 1
56
- end
50
+ def prev_sibling
51
+ return nil if parent.nil?
52
+ index = siblings.index(self)
53
+ siblings[index - 1]
54
+ end
57
55
 
58
- # Is this node the only descendant of its parent?
59
- def only_child?
60
- siblings.length == 1
61
- end
56
+ # Does this node have any siblings?
57
+ def has_siblings?
58
+ siblings.length > 1
59
+ end
62
60
 
63
- # Recursively get all descendant nodes, not including this node.
64
- def descendants
65
- children.map(&:subtree).flatten
66
- end
61
+ # Is this node the only descendant of its parent?
62
+ def only_child?
63
+ siblings.length == 1
64
+ end
67
65
 
68
- # Same as descendants, except it includes this node.
69
- def subtree
70
- [self] + descendants
71
- end
66
+ # Recursively get all descendant nodes, not including this node.
67
+ def descendants
68
+ children.map(&:subtree).flatten
69
+ end
72
70
 
73
- # Depth from the root node. Root depth is 0.
74
- def depth
75
- return ancestors.length + 1
76
- end
71
+ # Same as descendants, except it includes this node.
72
+ def subtree
73
+ [self] + descendants
74
+ end
77
75
 
78
- def path
79
- (ancestors.map(&:name) + [name]).join('/')
80
- end
76
+ # Depth from the root node. Root depth is 0.
77
+ def depth
78
+ return ancestors.length + 1
79
+ end
81
80
 
82
- def method_missing(method, *args, &block)
83
- test = /^(.+)_(layers|groups)$/.match(method)
84
- if test
85
- m = self.respond_to?(test[1]) ? test[1] : "#{test[1]}s"
86
- self.send(m).select &method("#{test[2]}_only")
87
- else
88
- super
81
+ def path(as_array = false)
82
+ path = (ancestors.map(&:name) + [name])
83
+ as_array ? path : path.join('/')
89
84
  end
90
- end
91
85
 
92
- private
86
+ def method_missing(method, *args, &block)
87
+ test = /^(.+)_(layers|groups)$/.match(method)
88
+ if test
89
+ m = self.respond_to?(test[1]) ? test[1] : "#{test[1]}s"
90
+ self.send(m).select &method("#{test[2]}_only")
91
+ else
92
+ super
93
+ end
94
+ end
95
+
96
+ private
93
97
 
94
- def layers_only(d); d.is_a?(PSD::Node::Layer); end
95
- def groups_only(d); d.is_a?(PSD::Node::Group); end
98
+ def layers_only(d); d.layer?; end
99
+ def groups_only(d); d.group?(false); end
100
+ end
96
101
  end
97
102
  end
98
103
  end