ase-palette 0.9.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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +39 -0
- data/LICENSE.txt +21 -0
- data/README.md +132 -0
- data/Rakefile +6 -0
- data/ase-palette.gemspec +46 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/ase-palette.rb +16 -0
- data/lib/ase-palette/color.rb +89 -0
- data/lib/ase-palette/group.rb +39 -0
- data/lib/ase-palette/palette.rb +166 -0
- data/lib/ase-palette/palette_binary.rb +210 -0
- data/lib/ase-palette/version.rb +3 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fefbb045aba878667c779f8cf8a72f21c9c91f919efad22a436bc27594a22a0d
|
4
|
+
data.tar.gz: 7bbc45674d4344377fbdda0057fbb0b174c2f9eca06d7c495b50a59889e172cb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e25b1c71ae0192350717faf8fe00cd438ba999cf27f8b70b9ca11547d9d3f103452c426b3fd02c132ef2cd5a9efd5e0db5e33061fef3a287b5a4d43d8a218a3c
|
7
|
+
data.tar.gz: 3a3de6e42b122ceb0f9c5db2f7c0c35e285e67af4e96e85645b56b56c57395c4e26f6b4ea9c5c181677de4a493186921acebb1ace0a81698d1f95393c0ce0471
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ase-palette (0.9.0)
|
5
|
+
bindata (~> 2.4.4)
|
6
|
+
hex_string (~> 1.0.1)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
bindata (2.4.4)
|
12
|
+
diff-lcs (1.3)
|
13
|
+
hex_string (1.0.1)
|
14
|
+
rake (11.3.0)
|
15
|
+
rspec (3.8.0)
|
16
|
+
rspec-core (~> 3.8.0)
|
17
|
+
rspec-expectations (~> 3.8.0)
|
18
|
+
rspec-mocks (~> 3.8.0)
|
19
|
+
rspec-core (3.8.0)
|
20
|
+
rspec-support (~> 3.8.0)
|
21
|
+
rspec-expectations (3.8.2)
|
22
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
23
|
+
rspec-support (~> 3.8.0)
|
24
|
+
rspec-mocks (3.8.0)
|
25
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
+
rspec-support (~> 3.8.0)
|
27
|
+
rspec-support (3.8.0)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
ase-palette!
|
34
|
+
bundler (~> 2.0)
|
35
|
+
rake (~> 11.2)
|
36
|
+
rspec (~> 3.8)
|
37
|
+
|
38
|
+
BUNDLED WITH
|
39
|
+
2.0.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Sam Becker
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# ASEPalette
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'ase-palette'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install ase-palette
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### Load
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'ase-palette'
|
25
|
+
```
|
26
|
+
|
27
|
+
### Write
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# Create palette
|
31
|
+
palette = ASEPalette.new
|
32
|
+
|
33
|
+
# Create color
|
34
|
+
color = ASEPalette::Color::RGB.new "Red", 255, 0, 0
|
35
|
+
|
36
|
+
# Add color to palette
|
37
|
+
palette.add_color color
|
38
|
+
|
39
|
+
# Add color to group
|
40
|
+
palette.add_color color, group_name: "Group Name"
|
41
|
+
```
|
42
|
+
|
43
|
+
### Read
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
# Access top-level colors
|
47
|
+
palette.colors.each do |color|
|
48
|
+
puts "Found color #{color.name}"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Access colors from groups
|
52
|
+
palette.groups.each do |group|
|
53
|
+
puts "Found group #{group.name}"
|
54
|
+
group.colors.each do |color|
|
55
|
+
puts "- #{color.name}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Access all colors, including those from groups, as a flat array
|
60
|
+
palette.colors(include_from_groups: true).each do |color|
|
61
|
+
puts "Found color #{color.name}, which may or may not belong to a group"
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### Edit
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# Remove a color, including those from groups
|
69
|
+
palette.remove_color_with_name "Red"
|
70
|
+
|
71
|
+
# Remove a group and all of the colors it contains
|
72
|
+
palette.remove_group_with_name "Group Name"
|
73
|
+
```
|
74
|
+
|
75
|
+
### Export
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# Store file
|
79
|
+
IO.binwrite('palette.ase', palette.to_binary)
|
80
|
+
|
81
|
+
# Send file (from Rails controller)
|
82
|
+
respond_to do |format|
|
83
|
+
format.ase {
|
84
|
+
send_data palette.to_binary, type: 'application/octet-stream', filename: 'palette.ase'
|
85
|
+
}
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
### Import [UNDER DEVELOPMENT]
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# Open palette from file
|
93
|
+
palette = ASEPalette.open('path/to/palette.ase')
|
94
|
+
|
95
|
+
# Access color
|
96
|
+
puts palette.color_with_name "Red"
|
97
|
+
# Red, RGB: 255/0/0, :global
|
98
|
+
|
99
|
+
# Access palette
|
100
|
+
puts palette
|
101
|
+
# ASEPalette 2.0
|
102
|
+
# --------------
|
103
|
+
#
|
104
|
+
# Red, RGB: 255/0/0, :global
|
105
|
+
#
|
106
|
+
# Group 1:
|
107
|
+
# Violet, RGB: 90/0/255, :global
|
108
|
+
# Blue, RGB: 0/0/255, :global
|
109
|
+
# Green, RGB: 50/255/50, :global
|
110
|
+
# Red, RGB: 240/0/20, :global
|
111
|
+
#
|
112
|
+
# Group 2
|
113
|
+
# Orange CMYK, CMYK: 0/80/100/0, :global
|
114
|
+
# Yellow CMYK, CMYK: 0/20/100/0, :global
|
115
|
+
#
|
116
|
+
# --------------
|
117
|
+
# 7 colors, 2 groups
|
118
|
+
```
|
119
|
+
|
120
|
+
## Development
|
121
|
+
|
122
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
123
|
+
|
124
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
125
|
+
|
126
|
+
## Contributing
|
127
|
+
|
128
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sambecker/ase-palette.
|
129
|
+
|
130
|
+
## License
|
131
|
+
|
132
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/ase-palette.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "ase-palette/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ase-palette"
|
8
|
+
spec.version = ASEPalette::VERSION
|
9
|
+
spec.authors = ["Sam Becker"]
|
10
|
+
spec.email = ["sam@sambecker.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Create and manage Adobe ASE palettes.}
|
13
|
+
spec.description = %q{Read and write Adobe Swatch Exchange files.}
|
14
|
+
spec.homepage = "https://github.com/sambecker/ase-palette"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
22
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
23
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/" \
|
24
|
+
"blob/master/CHANGELOG.md"
|
25
|
+
else
|
26
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
27
|
+
"public gem pushes."
|
28
|
+
end
|
29
|
+
|
30
|
+
# Specify which files should be added to the gem when it is released.
|
31
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
33
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
34
|
+
end
|
35
|
+
spec.bindir = "exe"
|
36
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
|
+
spec.require_paths = ["lib"]
|
38
|
+
spec.required_ruby_version = ">= 2.5.0"
|
39
|
+
|
40
|
+
spec.add_dependency "bindata", "~>2.4.4"
|
41
|
+
spec.add_dependency "hex_string", "~>1.0.1"
|
42
|
+
|
43
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
44
|
+
spec.add_development_dependency "rake", "~> 11.2"
|
45
|
+
spec.add_development_dependency "rspec", "~> 3.8"
|
46
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ase_palette"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/ase-palette.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bindata"
|
2
|
+
require "hex_string"
|
3
|
+
|
4
|
+
require "ase-palette/version"
|
5
|
+
require "ase-palette/palette"
|
6
|
+
require "ase-palette/palette_binary"
|
7
|
+
require "ase-palette/group"
|
8
|
+
require "ase-palette/color"
|
9
|
+
|
10
|
+
module ASEPalette
|
11
|
+
class Error < StandardError; end
|
12
|
+
|
13
|
+
def self.new
|
14
|
+
Palette.new
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module ASEPalette
|
2
|
+
DEFAULT_COLOR_TYPE = :global
|
3
|
+
|
4
|
+
class Color
|
5
|
+
# Name cannot changed once a color is created in order to
|
6
|
+
# protect the integrity of unique names in a palette
|
7
|
+
attr_reader :name
|
8
|
+
attr_accessor :type
|
9
|
+
|
10
|
+
# Get color model
|
11
|
+
def model
|
12
|
+
self.class.to_s.split('::').last.downcase.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
# Convert color to string
|
16
|
+
def to_s
|
17
|
+
"#{@name}, " \
|
18
|
+
"#{model.upcase}: " \
|
19
|
+
"#{data.values.join("/")}, " \
|
20
|
+
":#{@type}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Convert color to hash,
|
24
|
+
# necessary for binary representation
|
25
|
+
def to_h
|
26
|
+
{
|
27
|
+
name: @name,
|
28
|
+
model: model,
|
29
|
+
data: data,
|
30
|
+
type: @type,
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
class RGB < Color
|
35
|
+
attr_accessor :r, :g, :b
|
36
|
+
def initialize(name, r, g, b, type = DEFAULT_COLOR_TYPE)
|
37
|
+
@name = name
|
38
|
+
@r = r
|
39
|
+
@g = g
|
40
|
+
@b = b
|
41
|
+
@type = type
|
42
|
+
end
|
43
|
+
def data
|
44
|
+
{ r: @r, g: @g, b: @b }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class CMYK < Color
|
49
|
+
attr_accessor :c, :m, :y, :k
|
50
|
+
def initialize(name, c, m, y, k, type = DEFAULT_COLOR_TYPE)
|
51
|
+
@name = name
|
52
|
+
@c = c
|
53
|
+
@m = m
|
54
|
+
@y = y
|
55
|
+
@k = k
|
56
|
+
@type = type
|
57
|
+
end
|
58
|
+
def data
|
59
|
+
{ c: @c, m: @m, y: @y, k: @k }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class LAB < Color
|
64
|
+
attr_accessor :l, :a, :b
|
65
|
+
def initialize(name, l, a, b, type = DEFAULT_COLOR_TYPE)
|
66
|
+
@name = name
|
67
|
+
@l = l
|
68
|
+
@a = a
|
69
|
+
@b = b
|
70
|
+
@type = type
|
71
|
+
end
|
72
|
+
def data
|
73
|
+
{ l: @l, a: @a, b: @b }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class GRAY < Color
|
78
|
+
attr_accessor :gray
|
79
|
+
def initialize(name, gray, type = DEFAULT_COLOR_TYPE)
|
80
|
+
@name = name
|
81
|
+
@gray = gray
|
82
|
+
@type = type
|
83
|
+
end
|
84
|
+
def data
|
85
|
+
{ gray: @gray }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ASEPalette
|
2
|
+
class Group
|
3
|
+
# Name and colors cannot changed once a group is created in order to
|
4
|
+
# protect the integrity of unique names in a palette
|
5
|
+
attr_reader :name, :colors
|
6
|
+
|
7
|
+
# Initialize group
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
@colors = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Convert group to string
|
14
|
+
def to_s
|
15
|
+
s = "- #{@name}:\n"
|
16
|
+
if @colors.length > 0
|
17
|
+
@colors.each do |color|
|
18
|
+
s += " #{color}\n"
|
19
|
+
end
|
20
|
+
else
|
21
|
+
s += " <empty>\n"
|
22
|
+
end
|
23
|
+
s
|
24
|
+
end
|
25
|
+
|
26
|
+
# Convert group to hash,
|
27
|
+
# necessary for binary representation
|
28
|
+
def to_h
|
29
|
+
{
|
30
|
+
name: @name,
|
31
|
+
colors: @colors.map(&:to_h),
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove_color_with_name(name)
|
36
|
+
@colors = @colors.select { |color| color.name != name }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
module ASEPalette
|
2
|
+
class Palette
|
3
|
+
# TODO: Consider removing 'with_name' from method signatures
|
4
|
+
# TODO: Make sure to encode strings back to UTF-8 when importing ASE files
|
5
|
+
# TODO: Check case-sensitivity of the ASE spec for color and group names
|
6
|
+
|
7
|
+
# Initialize palette
|
8
|
+
def initialize
|
9
|
+
@version_major = 1
|
10
|
+
@version_minor = 0
|
11
|
+
@colors = []
|
12
|
+
@groups = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get palette version
|
16
|
+
def version
|
17
|
+
"#{@version_major}.#{@version_minor}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set palette version
|
21
|
+
def set_version(major, minor)
|
22
|
+
@version_major = major
|
23
|
+
@version_minor = minor
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get read-only list of colors
|
27
|
+
# Optionally include all colors from groups
|
28
|
+
def colors(include_from_groups: false)
|
29
|
+
if include_from_groups
|
30
|
+
all_colors
|
31
|
+
else
|
32
|
+
@colors.clone
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get color by name
|
37
|
+
def color_with_name(name)
|
38
|
+
found_colors = all_colors.select { |c| c.name == name }
|
39
|
+
found_colors.length >= 1 ? found_colors[0] : nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get read-only list of groups
|
43
|
+
def groups
|
44
|
+
@groups.clone
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get read-only group by name
|
48
|
+
def group_with_name(name)
|
49
|
+
found_groups = @groups.select { |g| g.name == name }
|
50
|
+
found_groups.length >= 1 ? found_groups[0].clone : nil
|
51
|
+
end
|
52
|
+
|
53
|
+
# Add color to palette
|
54
|
+
# Optionally provide 'group_name' to place color in group
|
55
|
+
# Group will be created if it does not exist
|
56
|
+
# Returns true if color is added
|
57
|
+
def add_color(color, group_name: nil)
|
58
|
+
if color.is_a? Color
|
59
|
+
if color_does_not_exist_in_palette(color.name)
|
60
|
+
if group_name
|
61
|
+
group = find_or_create_group(group_name)
|
62
|
+
group.colors << color
|
63
|
+
true
|
64
|
+
else
|
65
|
+
@colors << color
|
66
|
+
true
|
67
|
+
end
|
68
|
+
else
|
69
|
+
raise Error, "A color named #{color.name} already exists"
|
70
|
+
end
|
71
|
+
else
|
72
|
+
raise Error, "Argument 'color' is not of type #{ASEPalette::Color}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Create empty group in palette
|
77
|
+
# Returns true if group is created
|
78
|
+
def create_group(name)
|
79
|
+
if @groups.select { |group| group.name == name }.length == 0
|
80
|
+
@groups << ASEPalette::Group.new(name)
|
81
|
+
true
|
82
|
+
else
|
83
|
+
raise Error, "A group named #{name} already exists"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Remove color from palette
|
88
|
+
# Color may or may not be in a group
|
89
|
+
def remove_color_with_name(name)
|
90
|
+
@colors = @colors.select { |color| color.name != name }
|
91
|
+
@groups.each { |group| group.remove_color_with_name(name) }
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
# Remove group, and its colors, from palette
|
96
|
+
def remove_group_with_name(name)
|
97
|
+
@groups = @groups.select { |group| group.name != name }
|
98
|
+
true
|
99
|
+
end
|
100
|
+
|
101
|
+
# Create string representation of palette
|
102
|
+
def to_s
|
103
|
+
s = "ASEPalette #{version}\n"
|
104
|
+
divider = "#{"-" * (s.length - 1)}\n"
|
105
|
+
s += divider
|
106
|
+
if @colors.length > 0 || @groups.length > 0
|
107
|
+
s += "\n"
|
108
|
+
@colors.each do |color|
|
109
|
+
s += "#{color}\n"
|
110
|
+
end
|
111
|
+
s += "\n"
|
112
|
+
@groups.each do |group|
|
113
|
+
s += "#{group}\n"
|
114
|
+
end
|
115
|
+
else
|
116
|
+
s += "This palette is empty\n"
|
117
|
+
end
|
118
|
+
s += divider
|
119
|
+
s += "#{all_colors.length} color#{if all_colors.length != 1 then "s" end}, " \
|
120
|
+
"#{@groups.length} group#{if @groups.length != 1 then "s" end}"
|
121
|
+
s
|
122
|
+
end
|
123
|
+
|
124
|
+
# Create binary representation of palette
|
125
|
+
def to_binary
|
126
|
+
palette = PaletteBinary.build_binary_palette(
|
127
|
+
@colors.map(&:to_h),
|
128
|
+
@groups.map(&:to_h),
|
129
|
+
@version_major,
|
130
|
+
@version_minor,
|
131
|
+
)
|
132
|
+
palette.to_binary_s
|
133
|
+
end
|
134
|
+
|
135
|
+
# Create human-readable hex representation of palette
|
136
|
+
def to_hex
|
137
|
+
to_binary.to_hex_string
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# Returns an array of all colors in the palette,
|
143
|
+
# including those in groups
|
144
|
+
def all_colors
|
145
|
+
@colors + @groups.map { |group| group.colors }.flatten
|
146
|
+
end
|
147
|
+
|
148
|
+
# Determines whether or not a color exists in the palette,
|
149
|
+
# including those in groups
|
150
|
+
def color_does_not_exist_in_palette(name)
|
151
|
+
all_colors.select { |color| color.name == name }.length == 0
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns a found or created group
|
155
|
+
def find_or_create_group(name)
|
156
|
+
found_groups = @groups.select { |group| group.name == name }
|
157
|
+
if found_groups.length > 0
|
158
|
+
found_groups[0]
|
159
|
+
else
|
160
|
+
new_group = ASEPalette::Group.new(name)
|
161
|
+
@groups << new_group
|
162
|
+
new_group
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
module ASEPalette
|
2
|
+
module PaletteBinary
|
3
|
+
DEFAULT_VERSION_MAJOR = 1
|
4
|
+
DEFAULT_VERSION_MINOR = 0
|
5
|
+
|
6
|
+
BLOCK_TYPE_COLOR = 0x1
|
7
|
+
BLOCK_TYPE_GROUP_START = 0xC001
|
8
|
+
BLOCK_TYPE_GROUP_END = 0xC002
|
9
|
+
|
10
|
+
COLOR_TYPE_GLOBAL = 0
|
11
|
+
COLOR_TYPE_SPOT = 1
|
12
|
+
COLOR_TYPE_NORMAL = 2
|
13
|
+
COLOR_TYPE_DEFAULT = COLOR_TYPE_NORMAL
|
14
|
+
|
15
|
+
COLOR_MODEL_RGB = "RGB "
|
16
|
+
COLOR_MODEL_CMYK = "CMYK"
|
17
|
+
COLOR_MODEL_LAB = "LAB "
|
18
|
+
COLOR_MODEL_GRAY = "GRAY"
|
19
|
+
COLOR_MODEL_DEFAULT = COLOR_MODEL_RGB
|
20
|
+
|
21
|
+
class ASEBinData < BinData::Record
|
22
|
+
endian :big
|
23
|
+
|
24
|
+
string :signature, value: "ASEF"
|
25
|
+
uint16 :version_major, initial_value: DEFAULT_VERSION_MAJOR
|
26
|
+
uint16 :version_minor, initial_value: DEFAULT_VERSION_MINOR
|
27
|
+
uint32 :block_count, value: -> { blocks.length }
|
28
|
+
|
29
|
+
array :blocks do
|
30
|
+
uint16 :block_type
|
31
|
+
uint32 :block_length, value: -> {
|
32
|
+
block_length = 0
|
33
|
+
if block_type == BLOCK_TYPE_COLOR ||
|
34
|
+
block_type == BLOCK_TYPE_GROUP_START
|
35
|
+
block_length += block_data.name.length
|
36
|
+
end
|
37
|
+
if block_type == BLOCK_TYPE_COLOR
|
38
|
+
block_length += 10
|
39
|
+
case block_data.color_model
|
40
|
+
when COLOR_MODEL_RGB
|
41
|
+
block_length += 4 * 3
|
42
|
+
when COLOR_MODEL_CMYK, COLOR_MODEL_GRAY
|
43
|
+
block_length += 4 * 4
|
44
|
+
when COLOR_MODEL_LAB
|
45
|
+
block_length += 4 * 3
|
46
|
+
end
|
47
|
+
elsif block_type == BLOCK_TYPE_GROUP_START
|
48
|
+
block_length += 4
|
49
|
+
end
|
50
|
+
block_length
|
51
|
+
}
|
52
|
+
choice :block_data, selection: -> { block_type } do
|
53
|
+
class BlockGroupStart < BinData::Record
|
54
|
+
endian :big
|
55
|
+
uint16 :name_length, value: -> { name.length / 2 + 1 }
|
56
|
+
string :name, read_length: :name_length
|
57
|
+
uint16 :null_padding, value: 0
|
58
|
+
end
|
59
|
+
class BlockGroupEnd < BinData::Record
|
60
|
+
end
|
61
|
+
class BlockColor < BinData::Record
|
62
|
+
endian :big
|
63
|
+
uint16 :name_length, value: -> { name.length / 2 + 1 }
|
64
|
+
string :name, read_length: :name_length
|
65
|
+
uint16 :null_padding, value: 0
|
66
|
+
string :color_model, initial_value: COLOR_MODEL_DEFAULT
|
67
|
+
choice :color_data, selection: -> { color_model } do
|
68
|
+
class ColorDataRGB < BinData::Record
|
69
|
+
endian :big
|
70
|
+
float :red, initial_value: 0
|
71
|
+
float :green, initial_value: 0
|
72
|
+
float :blue, initial_value: 0
|
73
|
+
end
|
74
|
+
class ColorDataCMYK < BinData::Record
|
75
|
+
endian :big
|
76
|
+
float :cyan, initial_value: 0
|
77
|
+
float :magenta, initial_value: 0
|
78
|
+
float :yellow, initial_value: 0
|
79
|
+
float :black, initial_value: 0
|
80
|
+
end
|
81
|
+
class ColorDataLAB < BinData::Record
|
82
|
+
endian :big
|
83
|
+
float :lightness, initial_value: 0
|
84
|
+
float :a, initial_value: 0
|
85
|
+
float :b, initial_value: 0
|
86
|
+
end
|
87
|
+
|
88
|
+
ColorDataRGB COLOR_MODEL_RGB
|
89
|
+
ColorDataCMYK COLOR_MODEL_CMYK
|
90
|
+
ColorDataLAB COLOR_MODEL_LAB
|
91
|
+
# Grayscale data is stored in a CMYK structure
|
92
|
+
ColorDataCMYK COLOR_MODEL_GRAY
|
93
|
+
end
|
94
|
+
uint16 :color_type, initial_value: COLOR_TYPE_DEFAULT
|
95
|
+
end
|
96
|
+
BlockGroupStart BLOCK_TYPE_GROUP_START
|
97
|
+
BlockGroupEnd BLOCK_TYPE_GROUP_END
|
98
|
+
BlockColor BLOCK_TYPE_COLOR
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.build_binary_palette(
|
104
|
+
colors,
|
105
|
+
groups,
|
106
|
+
version_major = DEFAULT_VERSION_MAJOR,
|
107
|
+
version_minor = DEFAULT_VERSION_MINOR
|
108
|
+
)
|
109
|
+
palette = ASEBinData.new
|
110
|
+
palette.version_major = version_major
|
111
|
+
palette.version_minor = version_minor
|
112
|
+
colors.each do |color|
|
113
|
+
binary_add_color(
|
114
|
+
palette,
|
115
|
+
color[:name],
|
116
|
+
color[:model],
|
117
|
+
color[:type],
|
118
|
+
color[:data],
|
119
|
+
)
|
120
|
+
end
|
121
|
+
groups.each do |group|
|
122
|
+
binary_begin_group(palette, group[:name])
|
123
|
+
group[:colors].each do |color|
|
124
|
+
binary_add_color(
|
125
|
+
palette,
|
126
|
+
color[:name],
|
127
|
+
color[:model],
|
128
|
+
color[:type],
|
129
|
+
color[:data],
|
130
|
+
)
|
131
|
+
end
|
132
|
+
binary_end_group(palette)
|
133
|
+
end
|
134
|
+
palette
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def self.binary_add_color(palette, name, model, type, data)
|
140
|
+
case model
|
141
|
+
when :rgb
|
142
|
+
color_model = COLOR_MODEL_RGB
|
143
|
+
color_data = {
|
144
|
+
red: data[:r] / 255.0,
|
145
|
+
green: data[:g] / 255.0,
|
146
|
+
blue: data[:b] / 255.0,
|
147
|
+
}
|
148
|
+
when :cmyk
|
149
|
+
color_model = COLOR_MODEL_CMYK
|
150
|
+
color_data = {
|
151
|
+
cyan: data[:c] / 100.0,
|
152
|
+
magenta: data[:m] / 100.0,
|
153
|
+
yellow: data[:y] / 100.0,
|
154
|
+
black: data[:k] / 100.0,
|
155
|
+
}
|
156
|
+
when :lab
|
157
|
+
color_model = COLOR_MODEL_LAB
|
158
|
+
color_data = {
|
159
|
+
lightness: data[:l] / 100.0,
|
160
|
+
a: data[:a],
|
161
|
+
b: data[:b],
|
162
|
+
}
|
163
|
+
when :gray
|
164
|
+
# Grayscale is no longer supported by Adobe (was it ever?)
|
165
|
+
# This will default to a CMYK value with data only for black
|
166
|
+
color_model = COLOR_MODEL_CMYK
|
167
|
+
color_data = {
|
168
|
+
cyan: 0.0,
|
169
|
+
magenta: 0.0,
|
170
|
+
yellow: 0.0,
|
171
|
+
black: data[:gray] / 100.0,
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
case type
|
176
|
+
when :global
|
177
|
+
color_type = COLOR_TYPE_GLOBAL
|
178
|
+
when :spot
|
179
|
+
color_type = COLOR_TYPE_SPOT
|
180
|
+
when :normal
|
181
|
+
color_type = COLOR_TYPE_NORMAL
|
182
|
+
end
|
183
|
+
|
184
|
+
palette.blocks.push({
|
185
|
+
block_type: BLOCK_TYPE_COLOR,
|
186
|
+
block_data: {
|
187
|
+
name: name.encode(Encoding::UTF_16BE),
|
188
|
+
color_model: color_model,
|
189
|
+
color_data: color_data,
|
190
|
+
color_type: color_type,
|
191
|
+
}
|
192
|
+
})
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.binary_begin_group(palette, name)
|
196
|
+
palette.blocks.push({
|
197
|
+
block_type: BLOCK_TYPE_GROUP_START,
|
198
|
+
block_data: {
|
199
|
+
name: name.encode(Encoding::UTF_16BE),
|
200
|
+
}
|
201
|
+
})
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.binary_end_group(palette)
|
205
|
+
palette.blocks.push({
|
206
|
+
block_type: BLOCK_TYPE_GROUP_END,
|
207
|
+
})
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ase-palette
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Becker
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-03-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bindata
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.4.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.4.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: hex_string
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '11.2'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '11.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.8'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.8'
|
83
|
+
description: Read and write Adobe Swatch Exchange files.
|
84
|
+
email:
|
85
|
+
- sam@sambecker.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
93
|
+
- CHANGELOG.md
|
94
|
+
- Gemfile
|
95
|
+
- Gemfile.lock
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- ase-palette.gemspec
|
100
|
+
- bin/console
|
101
|
+
- bin/setup
|
102
|
+
- lib/ase-palette.rb
|
103
|
+
- lib/ase-palette/color.rb
|
104
|
+
- lib/ase-palette/group.rb
|
105
|
+
- lib/ase-palette/palette.rb
|
106
|
+
- lib/ase-palette/palette_binary.rb
|
107
|
+
- lib/ase-palette/version.rb
|
108
|
+
homepage: https://github.com/sambecker/ase-palette
|
109
|
+
licenses:
|
110
|
+
- MIT
|
111
|
+
metadata:
|
112
|
+
allowed_push_host: https://rubygems.org
|
113
|
+
homepage_uri: https://github.com/sambecker/ase-palette
|
114
|
+
source_code_uri: https://github.com/sambecker/ase-palette
|
115
|
+
changelog_uri: https://github.com/sambecker/ase-palette/blob/master/CHANGELOG.md
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.5.0
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
requirements: []
|
131
|
+
rubygems_version: 3.0.3
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: Create and manage Adobe ASE palettes.
|
135
|
+
test_files: []
|