psd 2.1.2 → 3.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/psd.rb +8 -6
- data/lib/psd/blend_mode.rb +46 -38
- data/lib/psd/channel_image.rb +9 -5
- data/lib/psd/descriptor.rb +39 -16
- data/lib/psd/header.rb +33 -32
- data/lib/psd/image_formats/rle.rb +4 -10
- data/lib/psd/image_modes/rgb.rb +4 -4
- data/lib/psd/layer.rb +1 -15
- data/lib/psd/layer/blend_modes.rb +12 -12
- data/lib/psd/layer/helpers.rb +8 -10
- data/lib/psd/layer/info.rb +9 -7
- data/lib/psd/layer/position_and_channels.rb +0 -4
- data/lib/psd/layer_info.rb +0 -4
- data/lib/psd/layer_info/blend_clipping_elements.rb +4 -2
- data/lib/psd/layer_info/blend_interior_elements.rb +4 -2
- data/lib/psd/layer_info/fill_opacity.rb +4 -2
- data/lib/psd/layer_info/layer_group.rb +4 -2
- data/lib/psd/layer_info/layer_id.rb +4 -2
- data/lib/psd/layer_info/layer_name_source.rb +4 -2
- data/lib/psd/layer_info/layer_section_divider.rb +4 -2
- data/lib/psd/layer_info/legacy_typetool.rb +5 -3
- data/lib/psd/layer_info/locked.rb +4 -2
- data/lib/psd/layer_info/metadata_setting.rb +4 -2
- data/lib/psd/layer_info/object_effects.rb +4 -2
- data/lib/psd/layer_info/pattern.rb +14 -0
- data/lib/psd/layer_info/placed_layer.rb +4 -2
- data/lib/psd/layer_info/reference_point.rb +4 -2
- data/lib/psd/layer_info/sheet_color.rb +18 -0
- data/lib/psd/layer_info/solid_color.rb +36 -0
- data/lib/psd/layer_info/typetool.rb +4 -2
- data/lib/psd/layer_info/unicode_name.rb +4 -2
- data/lib/psd/layer_info/vector_mask.rb +4 -2
- data/lib/psd/layer_info/vector_origination.rb +14 -0
- data/lib/psd/layer_info/vector_stroke.rb +4 -2
- data/lib/psd/layer_info/vector_stroke_content.rb +4 -2
- data/lib/psd/layer_mask.rb +2 -8
- data/lib/psd/lazy_execute.rb +5 -1
- data/lib/psd/node.rb +112 -48
- data/lib/psd/nodes/ancestry.rb +80 -75
- data/lib/psd/nodes/build_preview.rb +4 -4
- data/lib/psd/nodes/group.rb +35 -0
- data/lib/psd/nodes/layer.rb +40 -0
- data/lib/psd/nodes/root.rb +90 -0
- data/lib/psd/nodes/search.rb +19 -19
- data/lib/psd/path_record.rb +1 -71
- data/lib/psd/renderer.rb +6 -5
- data/lib/psd/renderer/blender.rb +10 -5
- data/lib/psd/renderer/cairo_helpers.rb +46 -0
- data/lib/psd/renderer/canvas.rb +39 -19
- data/lib/psd/renderer/canvas_management.rb +2 -2
- data/lib/psd/renderer/clipping_mask.rb +5 -4
- data/lib/psd/renderer/compose.rb +61 -68
- data/lib/psd/renderer/layer_styles.rb +15 -5
- data/lib/psd/renderer/layer_styles/color_overlay.rb +46 -27
- data/lib/psd/renderer/mask.rb +26 -22
- data/lib/psd/renderer/mask_canvas.rb +12 -0
- data/lib/psd/renderer/vector_shape.rb +239 -0
- data/lib/psd/resource_section.rb +4 -7
- data/lib/psd/resources.rb +4 -19
- data/lib/psd/resources/base.rb +27 -0
- data/lib/psd/resources/guides.rb +6 -4
- data/lib/psd/resources/layer_comps.rb +6 -4
- data/lib/psd/resources/slices.rb +7 -5
- data/lib/psd/version.rb +1 -1
- data/psd.gemspec +1 -2
- data/spec/files/blendmodes.psd +0 -0
- data/spec/hierarchy_spec.rb +5 -0
- metadata +27 -26
- data/lib/psd/layer_info/vector_mask_2.rb +0 -10
- data/lib/psd/node_exporting.rb +0 -20
- data/lib/psd/node_group.rb +0 -86
- data/lib/psd/node_layer.rb +0 -81
- data/lib/psd/node_root.rb +0 -93
- data/lib/psd/nodes/has_children.rb +0 -13
- data/lib/psd/nodes/lock_to_origin.rb +0 -7
- data/lib/psd/nodes/parse_layers.rb +0 -18
- data/lib/psd/renderer/layer_styles/drop_shadow.rb +0 -75
- data/lib/psd/section.rb +0 -26
data/lib/psd/layer_mask.rb
CHANGED
@@ -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.
|
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
|
-
|
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
|
data/lib/psd/lazy_execute.rb
CHANGED
@@ -41,7 +41,11 @@ class PSD
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def inspect
|
44
|
-
|
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
|
-
|
2
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
module Node
|
9
|
+
class Base
|
10
|
+
include Enumerable
|
11
|
+
include Ancestry
|
12
|
+
include Search
|
13
|
+
include BuildPreview
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
# Default properties that all nodes contain
|
16
|
+
PROPERTIES = [:name, :left, :right, :top, :bottom, :height, :width]
|
15
17
|
|
16
|
-
|
18
|
+
attr_reader :name, :parent
|
19
|
+
attr_accessor :children, :layer, :force_visible, :top_offset, :left_offset
|
17
20
|
|
18
|
-
|
19
|
-
|
21
|
+
delegate :psd, to: :parent
|
22
|
+
delegate :name, to: :layer
|
23
|
+
delegate :each, to: :children
|
24
|
+
delegate :document_dimensions, to: :parent
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
26
|
+
def initialize(layer, parent = nil)
|
27
|
+
@layer = layer
|
28
|
+
@layer.node = self
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
39
|
+
@top_offset = 0
|
40
|
+
@left_offset = 0
|
41
|
+
end
|
34
42
|
|
35
|
-
|
36
|
-
|
37
|
-
|
43
|
+
def top
|
44
|
+
@top + @top_offset
|
45
|
+
end
|
38
46
|
|
39
|
-
|
40
|
-
|
41
|
-
|
47
|
+
def bottom
|
48
|
+
@bottom + @top_offset
|
49
|
+
end
|
42
50
|
|
43
|
-
|
44
|
-
|
45
|
-
|
51
|
+
def left
|
52
|
+
@left + @left_offset
|
53
|
+
end
|
46
54
|
|
47
|
-
|
48
|
-
|
49
|
-
|
55
|
+
def right
|
56
|
+
@right + @left_offset
|
57
|
+
end
|
50
58
|
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
60
|
-
|
63
|
+
def height
|
64
|
+
bottom - top
|
61
65
|
end
|
62
66
|
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
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
|
data/lib/psd/nodes/ancestry.rb
CHANGED
@@ -1,98 +1,103 @@
|
|
1
1
|
class PSD
|
2
|
-
|
2
|
+
module Node
|
3
3
|
# Collection of methods to help in traversing the PSD tree structure.
|
4
4
|
module Ancestry
|
5
|
-
|
6
|
-
def root
|
7
|
-
return self if is_root?
|
8
|
-
return parent.root
|
9
|
-
end
|
5
|
+
extend ActiveSupport::Concern
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
# Does this node have any children nodes?
|
28
|
+
def has_children?
|
29
|
+
children.length > 0
|
30
|
+
end
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
parent.children
|
39
|
-
end
|
32
|
+
# Inverse of has_children?
|
33
|
+
def childless?
|
34
|
+
!has_children?
|
35
|
+
end
|
40
36
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
44
|
+
def next_sibling
|
45
|
+
return nil if parent.nil?
|
46
|
+
index = siblings.index(self)
|
47
|
+
siblings[index + 1]
|
48
|
+
end
|
52
49
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
50
|
+
def prev_sibling
|
51
|
+
return nil if parent.nil?
|
52
|
+
index = siblings.index(self)
|
53
|
+
siblings[index - 1]
|
54
|
+
end
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
# Does this node have any siblings?
|
57
|
+
def has_siblings?
|
58
|
+
siblings.length > 1
|
59
|
+
end
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
# Is this node the only descendant of its parent?
|
62
|
+
def only_child?
|
63
|
+
siblings.length == 1
|
64
|
+
end
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
66
|
+
# Recursively get all descendant nodes, not including this node.
|
67
|
+
def descendants
|
68
|
+
children.map(&:subtree).flatten
|
69
|
+
end
|
72
70
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
71
|
+
# Same as descendants, except it includes this node.
|
72
|
+
def subtree
|
73
|
+
[self] + descendants
|
74
|
+
end
|
77
75
|
|
78
|
-
|
79
|
-
|
80
|
-
|
76
|
+
# Depth from the root node. Root depth is 0.
|
77
|
+
def depth
|
78
|
+
return ancestors.length + 1
|
79
|
+
end
|
81
80
|
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
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
|
-
|
95
|
-
|
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
|