psd 3.4.0 → 3.5.0
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 +4 -4
- data/.travis.yml +1 -0
- data/lib/psd/color.rb +14 -5
- data/lib/psd/file.rb +4 -4
- data/lib/psd/layer/info.rb +46 -10
- data/lib/psd/layer/info/black_white.rb +33 -0
- data/lib/psd/layer/info/brightness_contrast.rb +21 -0
- data/lib/psd/layer/info/channel_mixer.rb +27 -0
- data/lib/psd/layer/info/color_balance.rb +23 -0
- data/lib/psd/layer/info/color_lookup.rb +14 -0
- data/lib/psd/layer/info/content_generator.rb +42 -0
- data/lib/psd/layer/info/curves.rb +78 -0
- data/lib/psd/layer/info/exposure.rb +20 -0
- data/lib/psd/layer/info/gradient_map.rb +69 -0
- data/lib/psd/layer/info/hue_saturation.rb +41 -0
- data/lib/psd/layer/info/invert.rb +17 -0
- data/lib/psd/layer/info/levels.rb +48 -0
- data/lib/psd/layer/info/pattern_fill.rb +15 -0
- data/lib/psd/layer/info/photo_filter.rb +44 -0
- data/lib/psd/layer/info/posterize.rb +16 -0
- data/lib/psd/layer/info/selective_color.rb +32 -0
- data/lib/psd/layer/info/sheet_color.rb +19 -1
- data/lib/psd/layer/info/threshold.rb +16 -0
- data/lib/psd/layer/info/vibrance.rb +22 -0
- data/lib/psd/mask.rb +5 -1
- data/lib/psd/node.rb +13 -0
- data/lib/psd/nodes/locking.rb +36 -0
- data/lib/psd/version.rb +1 -1
- data/spec/locked_spec.rb +8 -8
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94c708889f40e2d66fa1f271bbe5a2f385fcfef3
|
4
|
+
data.tar.gz: ff0cf841ac51420fe267285c725cc44191396fac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a315bcfd5bf734cfcb2d712b0df72569ee42e34fd593072c91923119ccdcd5a5b5b785ae250af2ef82dda47abf847131eeae4c658a870e7d661977a90cdfb12
|
7
|
+
data.tar.gz: 0666ac938c7f75cc01aff7e60f3fc3c6a7f28d366e4703199fa1ccc1dc292796b111cf877bc803d552db97c06d0b7ce6a151e003fef1afc2f28ecd87bcdc5b1e
|
data/.travis.yml
CHANGED
data/lib/psd/color.rb
CHANGED
@@ -6,8 +6,17 @@ class PSD
|
|
6
6
|
module Color
|
7
7
|
extend self
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
COLOR_SPACE = {
|
10
|
+
0 => :rgb,
|
11
|
+
1 => :hsb,
|
12
|
+
2 => :cmyk,
|
13
|
+
7 => :lab,
|
14
|
+
8 => :grayscale
|
15
|
+
}
|
16
|
+
|
17
|
+
# In some places in the PSD file, colors are stored with a short that
|
18
|
+
# describes the color space, and the following 4 bytes that store the
|
19
|
+
# color data.
|
11
20
|
def color_space_to_argb(color_space, color_component)
|
12
21
|
color = case color_space
|
13
22
|
when 0
|
@@ -20,7 +29,7 @@ class PSD
|
|
20
29
|
color_component[1] / 100.0, color_component[2] / 100.0,
|
21
30
|
color_component[3] / 100.0
|
22
31
|
when 7
|
23
|
-
|
32
|
+
alab_to_color *color_component
|
24
33
|
else
|
25
34
|
0x00FFFFFF
|
26
35
|
end
|
@@ -101,7 +110,7 @@ class PSD
|
|
101
110
|
|
102
111
|
def alab_to_color(alpha, l, a, b)
|
103
112
|
xyz = lab_to_xyz(l, a, b)
|
104
|
-
axyz_to_color alpha, xyz[
|
113
|
+
axyz_to_color alpha, xyz[0], xyz[1], xyz[2]
|
105
114
|
end
|
106
115
|
|
107
116
|
def lab_to_xyz(l, a, b)
|
@@ -122,4 +131,4 @@ class PSD
|
|
122
131
|
}.map { |k, v| [k, Util.clamp(v, 0, 255)] }]
|
123
132
|
end
|
124
133
|
end
|
125
|
-
end
|
134
|
+
end
|
data/lib/psd/file.rb
CHANGED
@@ -96,12 +96,12 @@ class PSD
|
|
96
96
|
# Reads a 32-bit color space value.
|
97
97
|
def read_space_color
|
98
98
|
color_space = read_short
|
99
|
-
|
100
|
-
4.times do |i|
|
101
|
-
|
99
|
+
color_components = []
|
100
|
+
4.times.map do |i|
|
101
|
+
color_components.push(read_short >> 8)
|
102
102
|
end
|
103
103
|
|
104
|
-
|
104
|
+
{ color_mode: color_space, color_components: color_components }
|
105
105
|
end
|
106
106
|
end
|
107
107
|
end
|
data/lib/psd/layer/info.rb
CHANGED
@@ -1,54 +1,90 @@
|
|
1
|
+
require 'psd/layer/info/black_white'
|
1
2
|
require 'psd/layer/info/blend_clipping_elements'
|
2
3
|
require 'psd/layer/info/blend_interior_elements'
|
4
|
+
require 'psd/layer/info/brightness_contrast'
|
5
|
+
require 'psd/layer/info/channel_mixer'
|
6
|
+
require 'psd/layer/info/color_balance'
|
7
|
+
require 'psd/layer/info/color_lookup'
|
8
|
+
require 'psd/layer/info/content_generator'
|
9
|
+
require 'psd/layer/info/curves'
|
10
|
+
require 'psd/layer/info/exposure'
|
3
11
|
require 'psd/layer/info/fill_opacity'
|
4
12
|
require 'psd/layer/info/gradient_fill'
|
13
|
+
require 'psd/layer/info/gradient_map'
|
14
|
+
require 'psd/layer/info/hue_saturation'
|
15
|
+
require 'psd/layer/info/invert'
|
5
16
|
require 'psd/layer/info/layer_group'
|
6
17
|
require 'psd/layer/info/layer_id'
|
7
18
|
require 'psd/layer/info/layer_name_source'
|
8
19
|
require 'psd/layer/info/layer_section_divider'
|
9
20
|
require 'psd/layer/info/legacy_typetool'
|
21
|
+
require 'psd/layer/info/levels'
|
10
22
|
require 'psd/layer/info/locked'
|
11
23
|
require 'psd/layer/info/metadata_setting'
|
12
24
|
require 'psd/layer/info/object_effects'
|
13
25
|
require 'psd/layer/info/pattern'
|
26
|
+
require 'psd/layer/info/pattern_fill'
|
27
|
+
require 'psd/layer/info/photo_filter'
|
14
28
|
require 'psd/layer/info/placed_layer'
|
29
|
+
require 'psd/layer/info/posterize'
|
15
30
|
require 'psd/layer/info/reference_point'
|
31
|
+
require 'psd/layer/info/selective_color'
|
16
32
|
require 'psd/layer/info/sheet_color'
|
17
33
|
require 'psd/layer/info/solid_color'
|
34
|
+
require 'psd/layer/info/threshold'
|
18
35
|
require 'psd/layer/info/typetool'
|
19
36
|
require 'psd/layer/info/unicode_name'
|
20
37
|
require 'psd/layer/info/vector_mask'
|
21
38
|
require 'psd/layer/info/vector_origination'
|
22
39
|
require 'psd/layer/info/vector_stroke'
|
23
40
|
require 'psd/layer/info/vector_stroke_content'
|
41
|
+
require 'psd/layer/info/vibrance'
|
24
42
|
|
25
43
|
class PSD
|
26
44
|
class Layer
|
27
45
|
module Info
|
28
46
|
# All of the extra layer info sections that we know how to parse.
|
29
47
|
LAYER_INFO = {
|
48
|
+
black_white: BlackWhite,
|
30
49
|
blend_clipping_elements: BlendClippingElements,
|
31
50
|
blend_interior_elements: BlendInteriorElements,
|
32
|
-
|
51
|
+
brightness_contrast: BrightnessContrast,
|
52
|
+
channel_mixer: ChannelMixer,
|
53
|
+
color_balance: ColorBalance,
|
54
|
+
color_lookup: ColorLookup,
|
55
|
+
content_generator: ContentGenerator,
|
56
|
+
curves: Curves,
|
57
|
+
exposure: Exposure,
|
58
|
+
fill_opacity: FillOpacity,
|
59
|
+
gradient_fill: GradientFill,
|
60
|
+
gradient_map: GradientMap,
|
61
|
+
hue_saturation: HueSaturation,
|
62
|
+
invert: Invert,
|
63
|
+
layer_id: LayerID,
|
64
|
+
layer_name_source: LayerNameSource,
|
33
65
|
legacy_type: LegacyTypeTool,
|
66
|
+
levels: Levels,
|
67
|
+
locked: Locked,
|
34
68
|
metadata: MetadataSetting,
|
35
|
-
layer_name_source: LayerNameSource,
|
36
|
-
object_effects: ObjectEffects,
|
37
69
|
name: UnicodeName,
|
38
|
-
section_divider: LayerSectionDivider,
|
39
|
-
sheet_color: SheetColor,
|
40
70
|
nested_section_divider: NestedLayerDivider,
|
41
|
-
|
42
|
-
|
43
|
-
|
71
|
+
object_effects: ObjectEffects,
|
72
|
+
pattern_fill: PatternFill,
|
73
|
+
photo_filter: PhotoFilter,
|
44
74
|
placed_layer: PlacedLayer,
|
45
|
-
|
75
|
+
posterize: Posterize,
|
76
|
+
reference_point: ReferencePoint,
|
77
|
+
selective_color: SelectiveColor,
|
78
|
+
section_divider: LayerSectionDivider,
|
79
|
+
sheet_color: SheetColor,
|
46
80
|
solid_color: SolidColor,
|
81
|
+
threshold: Threshold,
|
82
|
+
type: TypeTool,
|
47
83
|
vector_mask: VectorMask,
|
48
84
|
vector_origination: VectorOrigination,
|
49
85
|
vector_stroke: VectorStroke,
|
50
86
|
vector_stroke_content: VectorStrokeContent,
|
51
|
-
|
87
|
+
vibrance: Vibrance
|
52
88
|
}.freeze
|
53
89
|
|
54
90
|
BIG_LAYER_INFO_KEYS = %w{ LMsk Lr16 Lr32 Layr Mt16 Mt32 Mtrn Alph FMsk lnk2 FEid FXid PxSD }
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class BlackWhite < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'blwh'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :red, :yellow, :green, :cyan, :blue, :magenta
|
10
|
+
attr_reader :tint, :tint_color, :preset_id, :preset_name
|
11
|
+
|
12
|
+
def parse
|
13
|
+
@file.seek 4, IO::SEEK_CUR
|
14
|
+
@data = Descriptor.new(@file).parse
|
15
|
+
|
16
|
+
@red = @data['Rd ']
|
17
|
+
@yellow = @data['Yllw']
|
18
|
+
@green = @data['Grn ']
|
19
|
+
@cyan = @data['Cyn ']
|
20
|
+
@blue = @data['Bl ']
|
21
|
+
@magenta = @data['Mgnt']
|
22
|
+
@tint = @data['useTint']
|
23
|
+
@tint_color = {
|
24
|
+
red: @data['tintColor']['Rd '],
|
25
|
+
green: @data['tintColor']['Grn '],
|
26
|
+
blue: @data['tintColor']['Bl ']
|
27
|
+
}
|
28
|
+
|
29
|
+
@preset_id = @data['bwPresetKind']
|
30
|
+
@preset_name = @data['blackAndWhitePresetFileName']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
# NOTE: this only has the correct values when the "Use Legacy"
|
5
|
+
# checkbox is checked. If the 'CgEd' info key is present, these
|
6
|
+
# values will all be 0. Use that info block instead.
|
7
|
+
class BrightnessContrast < LayerInfo
|
8
|
+
def self.should_parse?(key)
|
9
|
+
key == 'brit'
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :brightness, :contrast, :mean_value, :lab_color
|
13
|
+
|
14
|
+
def parse
|
15
|
+
@brightness = @file.read_short
|
16
|
+
@contrast = @file.read_short
|
17
|
+
@mean_value = @file.read_short
|
18
|
+
@lab_color = @file.read_boolean
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class ChannelMixer < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'mixr'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :monochrome, :color
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@file.seek 2, IO::SEEK_CUR
|
13
|
+
|
14
|
+
@monochrome = @file.read_short > 0
|
15
|
+
|
16
|
+
@color = 4.times.map do
|
17
|
+
{
|
18
|
+
red_cyan: @file.read_short,
|
19
|
+
green_magenta: @file.read_short,
|
20
|
+
blue_yellow: @file.read_short,
|
21
|
+
black: @file.read_short,
|
22
|
+
constant: @file.read_short
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class ColorBalance < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'blnc'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :shadows, :midtones, :highlights, :preserve_luminosity
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@shadows, @midtones, @highlights = 3.times.map do
|
13
|
+
{
|
14
|
+
cyan_red: @file.read_short,
|
15
|
+
magenta_green: @file.read_short,
|
16
|
+
yellow_blue: @file.read_short
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
@preserve_luminosity = @file.read_short > 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
# This is the new way that Photoshop stores the brightness/contrast
|
5
|
+
# data. If this data is present, DO NOT use the legacy BrightnessContrast
|
6
|
+
# info block.
|
7
|
+
class ContentGenerator < LayerInfo
|
8
|
+
def self.should_parse?(key)
|
9
|
+
key == 'CgEd'
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
# Version
|
14
|
+
@file.seek 4, IO::SEEK_CUR
|
15
|
+
@data = Descriptor.new(@file).parse
|
16
|
+
end
|
17
|
+
|
18
|
+
def brightness
|
19
|
+
@data['Brgh']
|
20
|
+
end
|
21
|
+
|
22
|
+
def contrast
|
23
|
+
@data['Cntr']
|
24
|
+
end
|
25
|
+
|
26
|
+
def mean_value
|
27
|
+
@data['means']
|
28
|
+
end
|
29
|
+
|
30
|
+
def lab_color
|
31
|
+
@data['Lab ']
|
32
|
+
end
|
33
|
+
|
34
|
+
def use_legacy
|
35
|
+
@data['useLegacy']
|
36
|
+
end
|
37
|
+
|
38
|
+
def auto
|
39
|
+
@data['Auto']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Curves < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'curv'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :curves
|
10
|
+
|
11
|
+
def parse
|
12
|
+
# Padding, spec is wrong. Maybe Photoshop bug?
|
13
|
+
@file.seek 1, IO::SEEK_CUR
|
14
|
+
|
15
|
+
# Version
|
16
|
+
@file.seek 2, IO::SEEK_CUR
|
17
|
+
|
18
|
+
tag = @file.read_int
|
19
|
+
curve_count = 0
|
20
|
+
|
21
|
+
# Legacy data, it looks like there are 32 positions
|
22
|
+
# where you can adjust the curve, and there is a chunk
|
23
|
+
# of 32 bytes that determine whether that chunk is set.
|
24
|
+
32.times do |i|
|
25
|
+
curve_count += 1 if tag & (1 << i) > 0
|
26
|
+
end
|
27
|
+
|
28
|
+
@curves = []
|
29
|
+
curve_count.times do |i|
|
30
|
+
# Before each curve is a channel index
|
31
|
+
count = 0
|
32
|
+
curve = {}
|
33
|
+
32.times do |j|
|
34
|
+
if tag & (1 << j) > 0
|
35
|
+
if count == i
|
36
|
+
curve[:channel_index] = j
|
37
|
+
break
|
38
|
+
end
|
39
|
+
|
40
|
+
count += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
point_count = @file.read_short
|
45
|
+
curve[:points] = point_count.times.map do
|
46
|
+
{
|
47
|
+
output_value: @file.read_short,
|
48
|
+
input_value: @file.read_short
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
@curves << curve
|
53
|
+
end
|
54
|
+
|
55
|
+
if @file.tell < @section_end - 4
|
56
|
+
tag = @file.read_string(4)
|
57
|
+
raise "Extra curves key error: #{tag}" if tag != 'Crv '
|
58
|
+
|
59
|
+
@file.seek 2, IO::SEEK_CUR
|
60
|
+
|
61
|
+
curve_count = @file.read_int
|
62
|
+
curve_count.times do
|
63
|
+
curve = { channel_index: @file.read_short }
|
64
|
+
|
65
|
+
point_count = @file.read_short
|
66
|
+
curve[:points] = point_count.times.map do
|
67
|
+
{
|
68
|
+
output_value: @file.read_short,
|
69
|
+
input_value: @file.read_short
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
@curves << curve
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Exposure < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'expA'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :exposure, :offset, :gamma
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@file.seek 2, IO::SEEK_CUR
|
13
|
+
|
14
|
+
# Why this shit is big endian is beyond me. Thanks Adobe.
|
15
|
+
@exposure = @file.read(4).unpack('g')[0]
|
16
|
+
@offset = @file.read(4).unpack('g')[0]
|
17
|
+
@gamma = @file.read(4).unpack('g')[0]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class GradientMap < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'grdm'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :reverse, :dither, :name, :color_stops, :transparency_stops,
|
10
|
+
:interpolation, :mode, :random_seed, :showing_transparency, :using_vector_color,
|
11
|
+
:roughness_factor, :color_model, :minimum_color, :maximum_color
|
12
|
+
|
13
|
+
def parse
|
14
|
+
# Version
|
15
|
+
@file.seek 2, IO::SEEK_CUR
|
16
|
+
|
17
|
+
@reverse = @file.read_boolean
|
18
|
+
@dither = @file.read_boolean
|
19
|
+
|
20
|
+
@name = @file.read_unicode_string
|
21
|
+
|
22
|
+
color_stops = @file.read_short
|
23
|
+
@color_stops = color_stops.times.map do
|
24
|
+
color = {
|
25
|
+
location: @file.read_int,
|
26
|
+
midpoint: @file.read_int,
|
27
|
+
color: @file.read_space_color
|
28
|
+
}
|
29
|
+
|
30
|
+
# Mystery padding
|
31
|
+
@file.seek 2, IO::SEEK_CUR
|
32
|
+
color
|
33
|
+
end
|
34
|
+
|
35
|
+
transparency_stops = @file.read_short
|
36
|
+
@transparency_stops = transparency_stops.times.map do
|
37
|
+
{
|
38
|
+
location: @file.read_int,
|
39
|
+
midpoint: @file.read_int,
|
40
|
+
opacity: @file.read_short
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
expansion_count = @file.read_short
|
45
|
+
if expansion_count > 0
|
46
|
+
@interpolation = @file.read_short
|
47
|
+
length = @file.read_short
|
48
|
+
if length >= 32
|
49
|
+
@mode = @file.read_short
|
50
|
+
@random_seed = @file.read_int
|
51
|
+
@showing_transparency = @file.read_short > 0
|
52
|
+
@using_vector_color = @file.read_short > 0
|
53
|
+
@roughness_factor = @file.read_int
|
54
|
+
|
55
|
+
@color_model = @file.read_short
|
56
|
+
@minimum_color = 4.times.map do
|
57
|
+
@file.read_short >> 8
|
58
|
+
end
|
59
|
+
|
60
|
+
@maximum_color = 4.times.map do
|
61
|
+
@file.read_short >> 8
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@file.seek 2, IO::SEEK_CUR
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class HueSaturation < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'hue2'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :type, :colorization, :master, :range_values, :setting_values
|
10
|
+
|
11
|
+
def parse
|
12
|
+
# Version
|
13
|
+
@file.seek 2, IO::SEEK_CUR
|
14
|
+
|
15
|
+
@type = @file.read_byte == 0 ? :hue : :colorization
|
16
|
+
|
17
|
+
# Padding byte
|
18
|
+
@file.seek 1, IO::SEEK_CUR
|
19
|
+
|
20
|
+
@colorization = {
|
21
|
+
hue: @file.read_short,
|
22
|
+
saturation: @file.read_short,
|
23
|
+
lightness: @file.read_short
|
24
|
+
}
|
25
|
+
|
26
|
+
@master = {
|
27
|
+
hue: @file.read_short,
|
28
|
+
saturation: @file.read_short,
|
29
|
+
lightness: @file.read_short
|
30
|
+
}
|
31
|
+
|
32
|
+
@range_values = []
|
33
|
+
@setting_values = []
|
34
|
+
|
35
|
+
6.times do
|
36
|
+
@range_values << 4.times.map { @file.read_short }
|
37
|
+
@setting_values << 3.times.map { @file.read_short }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Invert < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'nvrt'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :inverted
|
10
|
+
|
11
|
+
def parse
|
12
|
+
# There is no data. The presence of this info block is
|
13
|
+
# all that's provided.
|
14
|
+
@inverted = true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Levels < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'levl'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :records
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@file.seek 2, IO::SEEK_CUR
|
13
|
+
|
14
|
+
@records = 29.times.map do
|
15
|
+
{
|
16
|
+
input_floor: @file.read_short,
|
17
|
+
input_ceiling: @file.read_short,
|
18
|
+
output_floor: @file.read_short,
|
19
|
+
output_ceiling: @file.read_short,
|
20
|
+
gamma: @file.read_short / 100.0,
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
# Photoshop CS (8.0) additional information
|
25
|
+
if @file.tell < @section_end - 4
|
26
|
+
tag = @file.read_string(4)
|
27
|
+
raise 'Extra levels key error' if tag != 'Lvls'
|
28
|
+
|
29
|
+
@file.seek 2, IO::SEEK_CUR
|
30
|
+
|
31
|
+
# Count of total level record structures. Subtract the legacy number of
|
32
|
+
# level record structures, 29, to determine how many are remaining in
|
33
|
+
# the file for reading.
|
34
|
+
extra_levels = @file.read_short - 29
|
35
|
+
|
36
|
+
extra_levels.times do
|
37
|
+
@records << {
|
38
|
+
input_floor: @file.read_short,
|
39
|
+
input_ceiling: @file.read_short,
|
40
|
+
output_floor: @file.read_short,
|
41
|
+
output_ceiling: @file.read_short,
|
42
|
+
gamma: @file.read_short / 100.0
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class PhotoFilter < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'phfl'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :color, :density, :preserve_luminosity
|
10
|
+
|
11
|
+
def parse
|
12
|
+
version = @file.read_short
|
13
|
+
|
14
|
+
case version
|
15
|
+
when 2 then parse_version_2
|
16
|
+
when 3 then parse_version_3
|
17
|
+
else return
|
18
|
+
end
|
19
|
+
|
20
|
+
@density = @file.read_int
|
21
|
+
@preserve_luminosity = @file.read_boolean
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def parse_version_2
|
27
|
+
color_space = @file.read_short
|
28
|
+
color_components = 4.times.map { @file.read_short }
|
29
|
+
|
30
|
+
@color = {
|
31
|
+
color_space: Color::COLOR_SPACE[color_space],
|
32
|
+
components: color_components
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_version_3
|
37
|
+
@color = {
|
38
|
+
x: @file.read_int >> 8,
|
39
|
+
y: @file.read_int >> 8,
|
40
|
+
z: @file.read_int >> 8
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Posterize < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'post'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :levels
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@levels = @file.read_short
|
13
|
+
@file.seek 2, IO::SEEK_CUR # Padding?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class SelectiveColor < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'selc'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :correction_mode, :cyan_correction, :magenta_correction,
|
10
|
+
:yellow_correction, :black_correction
|
11
|
+
|
12
|
+
def parse
|
13
|
+
@file.seek 2, IO::SEEK_CUR
|
14
|
+
|
15
|
+
@correction_mode = @file.read_short == 0 ? :relative : :absolute
|
16
|
+
@cyan_correction = []
|
17
|
+
@magenta_correction = []
|
18
|
+
@yellow_correction = []
|
19
|
+
@black_correction = []
|
20
|
+
|
21
|
+
10.times do |i|
|
22
|
+
# First record is all 0 and is ignored by Photoshop
|
23
|
+
@file.seek(8, IO::SEEK_CUR) and next if i == 0
|
24
|
+
|
25
|
+
@cyan_correction << @file.read_short
|
26
|
+
@magenta_correction << @file.read_short
|
27
|
+
@yellow_correction << @file.read_short
|
28
|
+
@black_correction << @file.read_short
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,12 +1,26 @@
|
|
1
1
|
require 'psd/layer_info'
|
2
2
|
|
3
3
|
class PSD
|
4
|
+
# This is the color label for a group/layer. Not sure why Adobe
|
5
|
+
# refers to it as the "Sheet Color".
|
4
6
|
class SheetColor < LayerInfo
|
5
7
|
def self.should_parse?(key)
|
6
8
|
key == 'lclr'
|
7
9
|
end
|
8
10
|
|
11
|
+
COLORS = [
|
12
|
+
:no_color,
|
13
|
+
:red,
|
14
|
+
:orange,
|
15
|
+
:yellow,
|
16
|
+
:green,
|
17
|
+
:blue,
|
18
|
+
:violet,
|
19
|
+
:gray
|
20
|
+
]
|
21
|
+
|
9
22
|
def parse
|
23
|
+
# Only the first entry is used, the rest are always 0.
|
10
24
|
@data = [
|
11
25
|
@file.read_short,
|
12
26
|
@file.read_short,
|
@@ -14,5 +28,9 @@ class PSD
|
|
14
28
|
@file.read_short
|
15
29
|
]
|
16
30
|
end
|
31
|
+
|
32
|
+
def color
|
33
|
+
COLORS[@data[0]]
|
34
|
+
end
|
17
35
|
end
|
18
|
-
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Threshold < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'thrs'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :level
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@level = @file.read_short
|
13
|
+
@file.seek 2, IO::SEEK_CUR # Padding?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Vibrance < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'vibA'
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse
|
10
|
+
@file.seek 4, IO::SEEK_CUR
|
11
|
+
@data = Descriptor.new(@file).parse
|
12
|
+
end
|
13
|
+
|
14
|
+
def vibrance
|
15
|
+
@data['vibrance']
|
16
|
+
end
|
17
|
+
|
18
|
+
def saturation
|
19
|
+
@data['Strt']
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/psd/mask.rb
CHANGED
data/lib/psd/node.rb
CHANGED
@@ -2,6 +2,7 @@ require 'psd/nodes/ancestry'
|
|
2
2
|
require 'psd/nodes/build_preview'
|
3
3
|
require 'psd/nodes/search'
|
4
4
|
require 'psd/nodes/layer_comps'
|
5
|
+
require 'psd/nodes/locking'
|
5
6
|
|
6
7
|
# Internal structure to help us build trees of a Photoshop documents.
|
7
8
|
# A lot of method names borrowed from the Ruby ancestry gem.
|
@@ -15,6 +16,7 @@ class PSD
|
|
15
16
|
include Search
|
16
17
|
include LayerComps
|
17
18
|
include BuildPreview
|
19
|
+
include Locking
|
18
20
|
|
19
21
|
# Default properties that all nodes contain
|
20
22
|
PROPERTIES = [:name, :left, :right, :top, :bottom, :height, :width]
|
@@ -91,6 +93,17 @@ class PSD
|
|
91
93
|
end
|
92
94
|
alias_method :clipped_by, :clipping_mask
|
93
95
|
|
96
|
+
# Color label is a little tricky. If you set the color of a group, all
|
97
|
+
# of it's descendants inhert the color unless manually overridden. So,
|
98
|
+
# if this node has no defined color, we have to walk up the ancestry tree
|
99
|
+
# to make sure the color isn't set somewhere else.
|
100
|
+
def color_label
|
101
|
+
color = layer.sheet_color.color
|
102
|
+
return color if color != :no_color || node.parent.root?
|
103
|
+
|
104
|
+
parent.color_label
|
105
|
+
end
|
106
|
+
|
94
107
|
def layer?
|
95
108
|
is_a?(PSD::Node::Layer)
|
96
109
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class PSD
|
2
|
+
module Node
|
3
|
+
# Locking is inherited by descendants, but Photoshop doesn't reflect
|
4
|
+
# this directly in the file, so we have to recursively traverse the
|
5
|
+
# ancestry tree to make sure an ancestor isn't locked.
|
6
|
+
module Locking
|
7
|
+
def all_locked?
|
8
|
+
return true if layer.all_locked?
|
9
|
+
return false if parent.root?
|
10
|
+
return parent.all_locked?
|
11
|
+
end
|
12
|
+
|
13
|
+
def any_locked?
|
14
|
+
position_locked? || composite_locked? || transparency_locked?
|
15
|
+
end
|
16
|
+
|
17
|
+
def position_locked?
|
18
|
+
return true if layer.position_locked?
|
19
|
+
return false if parent.root?
|
20
|
+
return parent.position_locked?
|
21
|
+
end
|
22
|
+
|
23
|
+
def composite_locked?
|
24
|
+
return true if layer.composite_locked?
|
25
|
+
return false if parent.root?
|
26
|
+
return parent.composite_locked?
|
27
|
+
end
|
28
|
+
|
29
|
+
def transparency_locked?
|
30
|
+
return true if layer.transparency_locked?
|
31
|
+
return false if parent.root?
|
32
|
+
return parent.transparency_locked?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/psd/version.rb
CHANGED
data/spec/locked_spec.rb
CHANGED
@@ -45,10 +45,10 @@ describe 'Locked' do
|
|
45
45
|
expect(psd.tree.children[7].composite_locked?).to eq(true)
|
46
46
|
expect(psd.tree.children[7].transparency_locked?).to eq(true)
|
47
47
|
|
48
|
-
expect(psd.tree.children[7].children[0].position_locked?).to eq(
|
49
|
-
expect(psd.tree.children[7].children[0].all_locked?).to eq(
|
50
|
-
expect(psd.tree.children[7].children[0].composite_locked?).to eq(
|
51
|
-
expect(psd.tree.children[7].children[0].transparency_locked?).to eq(
|
48
|
+
expect(psd.tree.children[7].children[0].position_locked?).to eq(true)
|
49
|
+
expect(psd.tree.children[7].children[0].all_locked?).to eq(true)
|
50
|
+
expect(psd.tree.children[7].children[0].composite_locked?).to eq(true)
|
51
|
+
expect(psd.tree.children[7].children[0].transparency_locked?).to eq(true)
|
52
52
|
|
53
53
|
expect(psd.tree.children[7].children[1].position_locked?).to eq(true)
|
54
54
|
expect(psd.tree.children[7].children[1].all_locked?).to eq(true)
|
@@ -56,9 +56,9 @@ describe 'Locked' do
|
|
56
56
|
expect(psd.tree.children[7].children[1].transparency_locked?).to eq(true)
|
57
57
|
|
58
58
|
expect(psd.tree.children[7].children[1].children[0].position_locked?).to eq(true)
|
59
|
-
expect(psd.tree.children[7].children[1].children[0].all_locked?).to eq(
|
60
|
-
expect(psd.tree.children[7].children[1].children[0].composite_locked?).to eq(
|
61
|
-
expect(psd.tree.children[7].children[1].children[0].transparency_locked?).to eq(
|
59
|
+
expect(psd.tree.children[7].children[1].children[0].all_locked?).to eq(true)
|
60
|
+
expect(psd.tree.children[7].children[1].children[0].composite_locked?).to eq(true)
|
61
|
+
expect(psd.tree.children[7].children[1].children[0].transparency_locked?).to eq(true)
|
62
62
|
|
63
63
|
expect(psd.tree.children[8].position_locked?).to eq(true)
|
64
64
|
expect(psd.tree.children[8].all_locked?).to eq(false)
|
@@ -75,4 +75,4 @@ describe 'Locked' do
|
|
75
75
|
expect(psd.tree.children[12].composite_locked?).to eq(false)
|
76
76
|
expect(psd.tree.children[12].transparency_locked?).to eq(false)
|
77
77
|
end
|
78
|
-
end
|
78
|
+
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: 3.
|
4
|
+
version: 3.5.0
|
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: 2015-
|
12
|
+
date: 2015-11-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -164,29 +164,47 @@ files:
|
|
164
164
|
- lib/psd/layer/exporting.rb
|
165
165
|
- lib/psd/layer/helpers.rb
|
166
166
|
- lib/psd/layer/info.rb
|
167
|
+
- lib/psd/layer/info/black_white.rb
|
167
168
|
- lib/psd/layer/info/blend_clipping_elements.rb
|
168
169
|
- lib/psd/layer/info/blend_interior_elements.rb
|
170
|
+
- lib/psd/layer/info/brightness_contrast.rb
|
171
|
+
- lib/psd/layer/info/channel_mixer.rb
|
172
|
+
- lib/psd/layer/info/color_balance.rb
|
173
|
+
- lib/psd/layer/info/color_lookup.rb
|
174
|
+
- lib/psd/layer/info/content_generator.rb
|
175
|
+
- lib/psd/layer/info/curves.rb
|
176
|
+
- lib/psd/layer/info/exposure.rb
|
169
177
|
- lib/psd/layer/info/fill_opacity.rb
|
170
178
|
- lib/psd/layer/info/gradient_fill.rb
|
179
|
+
- lib/psd/layer/info/gradient_map.rb
|
180
|
+
- lib/psd/layer/info/hue_saturation.rb
|
181
|
+
- lib/psd/layer/info/invert.rb
|
171
182
|
- lib/psd/layer/info/layer_group.rb
|
172
183
|
- lib/psd/layer/info/layer_id.rb
|
173
184
|
- lib/psd/layer/info/layer_name_source.rb
|
174
185
|
- lib/psd/layer/info/layer_section_divider.rb
|
175
186
|
- lib/psd/layer/info/legacy_typetool.rb
|
187
|
+
- lib/psd/layer/info/levels.rb
|
176
188
|
- lib/psd/layer/info/locked.rb
|
177
189
|
- lib/psd/layer/info/metadata_setting.rb
|
178
190
|
- lib/psd/layer/info/object_effects.rb
|
179
191
|
- lib/psd/layer/info/pattern.rb
|
192
|
+
- lib/psd/layer/info/pattern_fill.rb
|
193
|
+
- lib/psd/layer/info/photo_filter.rb
|
180
194
|
- lib/psd/layer/info/placed_layer.rb
|
195
|
+
- lib/psd/layer/info/posterize.rb
|
181
196
|
- lib/psd/layer/info/reference_point.rb
|
197
|
+
- lib/psd/layer/info/selective_color.rb
|
182
198
|
- lib/psd/layer/info/sheet_color.rb
|
183
199
|
- lib/psd/layer/info/solid_color.rb
|
200
|
+
- lib/psd/layer/info/threshold.rb
|
184
201
|
- lib/psd/layer/info/typetool.rb
|
185
202
|
- lib/psd/layer/info/unicode_name.rb
|
186
203
|
- lib/psd/layer/info/vector_mask.rb
|
187
204
|
- lib/psd/layer/info/vector_origination.rb
|
188
205
|
- lib/psd/layer/info/vector_stroke.rb
|
189
206
|
- lib/psd/layer/info/vector_stroke_content.rb
|
207
|
+
- lib/psd/layer/info/vibrance.rb
|
190
208
|
- lib/psd/layer/mask.rb
|
191
209
|
- lib/psd/layer/name.rb
|
192
210
|
- lib/psd/layer/path_components.rb
|
@@ -202,6 +220,7 @@ files:
|
|
202
220
|
- lib/psd/nodes/group.rb
|
203
221
|
- lib/psd/nodes/layer.rb
|
204
222
|
- lib/psd/nodes/layer_comps.rb
|
223
|
+
- lib/psd/nodes/locking.rb
|
205
224
|
- lib/psd/nodes/root.rb
|
206
225
|
- lib/psd/nodes/search.rb
|
207
226
|
- lib/psd/path_record.rb
|