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