psd 2.1.2 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
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