color_contrast_calc 0.8.0 → 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 +4 -4
- data/.rubocop.yml +1 -1
- data/.travis.yml +10 -3
- data/README.ja.md +18 -9
- data/README.md +18 -9
- data/color_contrast_calc.gemspec +5 -5
- data/examples/sort_colors.rb +17 -9
- data/lib/color_contrast_calc/color.rb +36 -20
- data/lib/color_contrast_calc/color_function_parser.rb +86 -26
- data/lib/color_contrast_calc/invalid_color_representation_error.rb +5 -1
- data/lib/color_contrast_calc/sorter.rb +151 -107
- data/lib/color_contrast_calc/transparency_calc.rb +1 -1
- data/lib/color_contrast_calc/utils.rb +58 -8
- data/lib/color_contrast_calc/version.rb +1 -1
- metadata +12 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07b766d958883d015466220f97ed047512e0fc6dd2a51288b5274c6b0287f0fe
|
4
|
+
data.tar.gz: 1e740d3598c423f07efb3632b31bf155dbf337f8f9265ede39f3b12bd3e50456
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d3becf7316ad107082efb72a1e777fb23272a4fe73239170296e55e978d7d0935ad23e7b0a9203c0dc94164ac2dca5efc5763139c49e19cf116c50c28539e1c
|
7
|
+
data.tar.gz: 6e3e1efa303a5ea42e56fd4011ccee3cd19677f0ae770a279c9a4b6ffa9340bb63d5d192224b4f7b18a1364e2b299ecab878dee3a76aa657843ee333a619341b
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
- 2.3
|
5
|
-
- 2.4
|
6
4
|
- 2.5
|
7
5
|
- 2.6
|
8
6
|
- 2.7
|
7
|
+
- 3.0
|
9
8
|
- ruby-head
|
10
|
-
- jruby-head
|
11
9
|
before_install: gem install bundler -v '~>2.0'
|
10
|
+
jobs:
|
11
|
+
include:
|
12
|
+
- rvm: 2.4
|
13
|
+
before_install:
|
14
|
+
- yes | gem update --system --force
|
15
|
+
- gem install bundler
|
16
|
+
- rvm: jruby-head
|
17
|
+
before_install:
|
18
|
+
- bundle add rake --version "13.0.0"
|
data/README.ja.md
CHANGED
@@ -289,32 +289,40 @@ The grayscale of #ffa500 is #acacac.
|
|
289
289
|
```ruby
|
290
290
|
require 'color_contrast_calc'
|
291
291
|
|
292
|
-
color_names = ['red', '
|
293
|
-
colors = color_names.map {|c| ColorContrastCalc.color_from(c) }
|
292
|
+
color_names = ['red', 'lime', 'cyan', 'yellow', 'fuchsia', 'blue']
|
294
293
|
|
295
294
|
# Sort by hSL order. An uppercase for a component of color means
|
296
295
|
# that component should be sorted in descending order.
|
297
296
|
|
298
|
-
hsl_ordered = ColorContrastCalc.sort(
|
299
|
-
puts("Colors sorted in the order of hSL: #{hsl_ordered
|
297
|
+
hsl_ordered = ColorContrastCalc.sort(color_names, 'hSL')
|
298
|
+
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
300
299
|
|
301
300
|
# Sort by RGB order.
|
302
301
|
|
303
|
-
rgb_ordered = ColorContrastCalc.sort(
|
304
|
-
puts("Colors sorted in the order of RGB: #{rgb_ordered
|
302
|
+
rgb_ordered = ColorContrastCalc.sort(color_names, 'RGB')
|
303
|
+
puts("Colors sorted in the order of RGB: #{rgb_ordered}")
|
305
304
|
|
306
305
|
# You can also change the precedence of components.
|
307
306
|
|
308
|
-
grb_ordered = ColorContrastCalc.sort(
|
309
|
-
puts("Colors sorted in the order of GRB: #{grb_ordered
|
307
|
+
grb_ordered = ColorContrastCalc.sort(color_names, 'GRB')
|
308
|
+
puts("Colors sorted in the order of GRB: #{grb_ordered}")
|
310
309
|
|
311
310
|
# And you can directly sort hex color codes.
|
312
311
|
|
313
312
|
## Hex color codes that correspond to the color_names given above.
|
314
|
-
hex_codes = ['#ff0000', '#
|
313
|
+
hex_codes = ['#ff0000', '#00ff00', '#0ff', '#ff0', '#f0f', '#0000FF']
|
315
314
|
|
316
315
|
hsl_ordered = ColorContrastCalc.sort(hex_codes, 'hSL')
|
317
316
|
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
317
|
+
|
318
|
+
# If you want to sort colors in different notations,
|
319
|
+
# you should specify a key_mapper.
|
320
|
+
|
321
|
+
colors = ['rgb(255 0 0)', 'hsl(120 100% 50%)', '#0ff', 'hwb(60 0% 0%)', [255, 0, 255], '#0000ff']
|
322
|
+
|
323
|
+
key_mapper = proc {|c| ColorContrastCalc.color_from(c) }
|
324
|
+
colors_in_hsl_order = ColorContrastCalc.sort(colors, 'hSL', key_mapper)
|
325
|
+
puts("Colors sorted in the order of hSL: #{colors_in_hsl_order}")
|
318
326
|
```
|
319
327
|
|
320
328
|
以下のように実行します:
|
@@ -325,6 +333,7 @@ Colors sorted in the order of hSL: ["red", "yellow", "lime", "cyan", "blue", "fu
|
|
325
333
|
Colors sorted in the order of RGB: ["yellow", "fuchsia", "red", "cyan", "lime", "blue"]
|
326
334
|
Colors sorted in the order of GRB: ["yellow", "cyan", "lime", "fuchsia", "red", "blue"]
|
327
335
|
Colors sorted in the order of hSL: ["#ff0000", "#ff0", "#00ff00", "#0ff", "#0000FF", "#f0f"]
|
336
|
+
Colors sorted in the order of hSL: ["rgb(255 0 0)", "hwb(60 0% 0%)", "hsl(120 100% 50%)", "#0ff", "#0000ff", [255, 0, 255]]
|
328
337
|
```
|
329
338
|
|
330
339
|
### 例5: 定義済みの色のリスト
|
data/README.md
CHANGED
@@ -292,32 +292,40 @@ For example, save the following code as `sort_colors.rb`:
|
|
292
292
|
```ruby
|
293
293
|
require 'color_contrast_calc'
|
294
294
|
|
295
|
-
color_names = ['red', '
|
296
|
-
colors = color_names.map {|c| ColorContrastCalc.color_from(c) }
|
295
|
+
color_names = ['red', 'lime', 'cyan', 'yellow', 'fuchsia', 'blue']
|
297
296
|
|
298
297
|
# Sort by hSL order. An uppercase for a component of color means
|
299
298
|
# that component should be sorted in descending order.
|
300
299
|
|
301
|
-
hsl_ordered = ColorContrastCalc.sort(
|
302
|
-
puts("Colors sorted in the order of hSL: #{hsl_ordered
|
300
|
+
hsl_ordered = ColorContrastCalc.sort(color_names, 'hSL')
|
301
|
+
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
303
302
|
|
304
303
|
# Sort by RGB order.
|
305
304
|
|
306
|
-
rgb_ordered = ColorContrastCalc.sort(
|
307
|
-
puts("Colors sorted in the order of RGB: #{rgb_ordered
|
305
|
+
rgb_ordered = ColorContrastCalc.sort(color_names, 'RGB')
|
306
|
+
puts("Colors sorted in the order of RGB: #{rgb_ordered}")
|
308
307
|
|
309
308
|
# You can also change the precedence of components.
|
310
309
|
|
311
|
-
grb_ordered = ColorContrastCalc.sort(
|
312
|
-
puts("Colors sorted in the order of GRB: #{grb_ordered
|
310
|
+
grb_ordered = ColorContrastCalc.sort(color_names, 'GRB')
|
311
|
+
puts("Colors sorted in the order of GRB: #{grb_ordered}")
|
313
312
|
|
314
313
|
# And you can directly sort hex color codes.
|
315
314
|
|
316
315
|
## Hex color codes that correspond to the color_names given above.
|
317
|
-
hex_codes = ['#ff0000', '#
|
316
|
+
hex_codes = ['#ff0000', '#00ff00', '#0ff', '#ff0', '#f0f', '#0000FF']
|
318
317
|
|
319
318
|
hsl_ordered = ColorContrastCalc.sort(hex_codes, 'hSL')
|
320
319
|
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
320
|
+
|
321
|
+
# If you want to sort colors in different notations,
|
322
|
+
# you should specify a key_mapper.
|
323
|
+
|
324
|
+
colors = ['rgb(255 0 0)', 'hsl(120 100% 50%)', '#0ff', 'hwb(60 0% 0%)', [255, 0, 255], '#0000ff']
|
325
|
+
|
326
|
+
key_mapper = proc {|c| ColorContrastCalc.color_from(c) }
|
327
|
+
colors_in_hsl_order = ColorContrastCalc.sort(colors, 'hSL', key_mapper)
|
328
|
+
puts("Colors sorted in the order of hSL: #{colors_in_hsl_order}")
|
321
329
|
```
|
322
330
|
|
323
331
|
Then execute the script:
|
@@ -328,6 +336,7 @@ Colors sorted in the order of hSL: ["red", "yellow", "lime", "cyan", "blue", "fu
|
|
328
336
|
Colors sorted in the order of RGB: ["yellow", "fuchsia", "red", "cyan", "lime", "blue"]
|
329
337
|
Colors sorted in the order of GRB: ["yellow", "cyan", "lime", "fuchsia", "red", "blue"]
|
330
338
|
Colors sorted in the order of hSL: ["#ff0000", "#ff0", "#00ff00", "#0ff", "#0000FF", "#f0f"]
|
339
|
+
Colors sorted in the order of hSL: ["rgb(255 0 0)", "hwb(60 0% 0%)", "hsl(120 100% 50%)", "#0ff", "#0000ff", [255, 0, 255]]
|
331
340
|
```
|
332
341
|
|
333
342
|
### Example 5: Lists of predefined colors
|
data/color_contrast_calc.gemspec
CHANGED
@@ -7,7 +7,7 @@ require 'color_contrast_calc/version'
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'color_contrast_calc'
|
9
9
|
spec.version = ColorContrastCalc::VERSION
|
10
|
-
spec.required_ruby_version = ">= 2.
|
10
|
+
spec.required_ruby_version = ">= 2.4"
|
11
11
|
spec.authors = ['HASHIMOTO, Naoki']
|
12
12
|
spec.email = ['hashimoto.naoki@gmail.com']
|
13
13
|
|
@@ -23,9 +23,9 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
-
spec.add_development_dependency 'bundler', '~> 2.
|
26
|
+
spec.add_development_dependency 'bundler', '~> 2.1'
|
27
27
|
spec.add_development_dependency 'rake', '~> 13.0'
|
28
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
29
|
-
spec.add_development_dependency 'rubocop', '~>
|
30
|
-
spec.add_development_dependency 'yard', '~> 0.9
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.10'
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 1.7'
|
30
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
31
31
|
end
|
data/examples/sort_colors.rb
CHANGED
@@ -2,29 +2,37 @@
|
|
2
2
|
|
3
3
|
require 'color_contrast_calc'
|
4
4
|
|
5
|
-
color_names = ['red', '
|
6
|
-
colors = color_names.map {|c| ColorContrastCalc.color_from(c) }
|
5
|
+
color_names = ['red', 'lime', 'cyan', 'yellow', 'fuchsia', 'blue']
|
7
6
|
|
8
7
|
# Sort by hSL order. An uppercase for a component of color means
|
9
8
|
# that component should be sorted in descending order.
|
10
9
|
|
11
|
-
hsl_ordered = ColorContrastCalc.sort(
|
12
|
-
puts("Colors sorted in the order of hSL: #{hsl_ordered
|
10
|
+
hsl_ordered = ColorContrastCalc.sort(color_names, 'hSL')
|
11
|
+
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
13
12
|
|
14
13
|
# Sort by RGB order.
|
15
14
|
|
16
|
-
rgb_ordered = ColorContrastCalc.sort(
|
17
|
-
puts("Colors sorted in the order of RGB: #{rgb_ordered
|
15
|
+
rgb_ordered = ColorContrastCalc.sort(color_names, 'RGB')
|
16
|
+
puts("Colors sorted in the order of RGB: #{rgb_ordered}")
|
18
17
|
|
19
18
|
# You can also change the precedence of components.
|
20
19
|
|
21
|
-
grb_ordered = ColorContrastCalc.sort(
|
22
|
-
puts("Colors sorted in the order of GRB: #{grb_ordered
|
20
|
+
grb_ordered = ColorContrastCalc.sort(color_names, 'GRB')
|
21
|
+
puts("Colors sorted in the order of GRB: #{grb_ordered}")
|
23
22
|
|
24
23
|
# And you can directly sort hex color codes.
|
25
24
|
|
26
25
|
## Hex color codes that correspond to the color_names given above.
|
27
|
-
hex_codes = ['#ff0000', '#
|
26
|
+
hex_codes = ['#ff0000', '#00ff00', '#0ff', '#ff0', '#f0f', '#0000FF']
|
28
27
|
|
29
28
|
hsl_ordered = ColorContrastCalc.sort(hex_codes, 'hSL')
|
30
29
|
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
30
|
+
|
31
|
+
# If you want to sort colors in different notations,
|
32
|
+
# you should specify a key_mapper.
|
33
|
+
|
34
|
+
colors = ['rgb(255 0 0)', 'hsl(120 100% 50%)', '#0ff', 'hwb(60 0% 0%)', [255, 0, 255], '#0000ff']
|
35
|
+
|
36
|
+
key_mapper = proc {|c| ColorContrastCalc.color_from(c) }
|
37
|
+
colors_in_hsl_order = ColorContrastCalc.sort(colors, 'hSL', key_mapper)
|
38
|
+
puts("Colors sorted in the order of hSL: #{colors_in_hsl_order}")
|
@@ -45,8 +45,7 @@ module ColorContrastCalc
|
|
45
45
|
# @return [Color] Instance of Color
|
46
46
|
|
47
47
|
def from_rgb(rgb, name = nil)
|
48
|
-
!name && List::HEX_TO_COLOR[Utils.rgb_to_hex(rgb)] ||
|
49
|
-
Color.new(rgb, name)
|
48
|
+
!name && List::HEX_TO_COLOR[Utils.rgb_to_hex(rgb)] || new(rgb, name)
|
50
49
|
end
|
51
50
|
|
52
51
|
##
|
@@ -58,8 +57,7 @@ module ColorContrastCalc
|
|
58
57
|
|
59
58
|
def from_hex(hex, name = nil)
|
60
59
|
normalized_hex = Utils.normalize_hex(hex)
|
61
|
-
!name && List::HEX_TO_COLOR[normalized_hex] ||
|
62
|
-
Color.new(normalized_hex, name)
|
60
|
+
!name && List::HEX_TO_COLOR[normalized_hex] || new(normalized_hex, name)
|
63
61
|
end
|
64
62
|
|
65
63
|
##
|
@@ -72,12 +70,11 @@ module ColorContrastCalc
|
|
72
70
|
def from_hsl(hsl, name = nil)
|
73
71
|
if hsl.length == 4
|
74
72
|
rgb = Utils.hsl_to_rgb(hsl[0, 3])
|
75
|
-
return
|
73
|
+
return new(rgb.push(hsl.last), name) unless opaque?(hsl)
|
76
74
|
end
|
77
75
|
|
78
76
|
rgb ||= Utils.hsl_to_rgb(hsl)
|
79
|
-
!name && List::HEX_TO_COLOR[Utils.rgb_to_hex(rgb)] ||
|
80
|
-
Color.new(rgb, name)
|
77
|
+
!name && List::HEX_TO_COLOR[Utils.rgb_to_hex(rgb)] || new(rgb, name)
|
81
78
|
end
|
82
79
|
|
83
80
|
##
|
@@ -141,19 +138,19 @@ module ColorContrastCalc
|
|
141
138
|
end
|
142
139
|
|
143
140
|
def opaque?(color_value)
|
144
|
-
color_value[-1] ==
|
141
|
+
color_value[-1] == Utils::MAX_OPACITY
|
145
142
|
end
|
146
143
|
|
147
144
|
private :opaque?
|
148
145
|
|
149
146
|
def color_from_rgba(rgba_value, name = nil)
|
150
147
|
unless Utils.valid_rgb?(rgba_value[0, 3])
|
151
|
-
raise
|
148
|
+
raise InvalidColorRepresentationError.from_value(rgba_value)
|
152
149
|
end
|
153
150
|
|
154
151
|
return color_from_rgb(rgba_value[0, 3], name) if opaque?(rgba_value)
|
155
152
|
|
156
|
-
|
153
|
+
new(rgba_value, name)
|
157
154
|
end
|
158
155
|
|
159
156
|
private :color_from_rgba
|
@@ -164,7 +161,7 @@ module ColorContrastCalc
|
|
164
161
|
end
|
165
162
|
|
166
163
|
hex_code = Utils.rgb_to_hex(rgb_value)
|
167
|
-
!name && List::HEX_TO_COLOR[hex_code] ||
|
164
|
+
!name && List::HEX_TO_COLOR[hex_code] || new(rgb_value, name)
|
168
165
|
end
|
169
166
|
|
170
167
|
private :color_from_rgb
|
@@ -193,7 +190,7 @@ module ColorContrastCalc
|
|
193
190
|
end
|
194
191
|
|
195
192
|
hex_code = Utils.normalize_hex(color_value)
|
196
|
-
!name && List::HEX_TO_COLOR[hex_code] ||
|
193
|
+
!name && List::HEX_TO_COLOR[hex_code] || new(hex_code, name)
|
197
194
|
end
|
198
195
|
|
199
196
|
private :color_from_str
|
@@ -234,6 +231,12 @@ module ColorContrastCalc
|
|
234
231
|
@relative_luminance = Checker.relative_luminance(@rgb)
|
235
232
|
end
|
236
233
|
|
234
|
+
def create(rgb, name = nil)
|
235
|
+
self.class.new(rgb, name)
|
236
|
+
end
|
237
|
+
|
238
|
+
private :create
|
239
|
+
|
237
240
|
##
|
238
241
|
# Return HSL value of the color.
|
239
242
|
#
|
@@ -248,6 +251,20 @@ module ColorContrastCalc
|
|
248
251
|
@hsl ||= Utils.rgb_to_hsl(@rgb)
|
249
252
|
end
|
250
253
|
|
254
|
+
##
|
255
|
+
# Return HWB value of the color.
|
256
|
+
#
|
257
|
+
# The value is calculated from the RGB value, so if you create
|
258
|
+
# the instance by Color.color_from method, the value used to
|
259
|
+
# create the color does not necessarily correspond to the value
|
260
|
+
# of this property.
|
261
|
+
#
|
262
|
+
# @return [Array<Float>] HWB value represented as an array of numbers
|
263
|
+
|
264
|
+
def hwb
|
265
|
+
@hwb ||= Utils.rgb_to_hwb(@rgb)
|
266
|
+
end
|
267
|
+
|
251
268
|
##
|
252
269
|
# Return a {https://www.w3.org/TR/SVG/types.html#ColorKeywords
|
253
270
|
# color keyword name} when the name corresponds to the hex code
|
@@ -256,8 +273,7 @@ module ColorContrastCalc
|
|
256
273
|
# @return [String] Color keyword name or hex color code
|
257
274
|
|
258
275
|
def common_name
|
259
|
-
|
260
|
-
named_color && named_color.name || @hex
|
276
|
+
List::HEX_TO_COLOR[@hex]&.name || @hex
|
261
277
|
end
|
262
278
|
|
263
279
|
##
|
@@ -347,7 +363,7 @@ module ColorContrastCalc
|
|
347
363
|
# @return [Color] New complementary color
|
348
364
|
def complementary(name = nil)
|
349
365
|
minmax = rgb.minmax.reduce {|min, max| min + max }
|
350
|
-
|
366
|
+
create(rgb.map {|c| minmax - c }, name)
|
351
367
|
end
|
352
368
|
|
353
369
|
##
|
@@ -363,8 +379,8 @@ module ColorContrastCalc
|
|
363
379
|
# of +other_color+
|
364
380
|
|
365
381
|
def find_brightness_threshold(other_color, level = Checker::Level::AA)
|
366
|
-
other_color =
|
367
|
-
|
382
|
+
other_color = create(other_color) unless other_color.is_a? Color
|
383
|
+
create(ThresholdFinder::Brightness.find(rgb, other_color.rgb, level))
|
368
384
|
end
|
369
385
|
|
370
386
|
##
|
@@ -380,8 +396,8 @@ module ColorContrastCalc
|
|
380
396
|
# of +other_color+
|
381
397
|
|
382
398
|
def find_lightness_threshold(other_color, level = Checker::Level::AA)
|
383
|
-
other_color =
|
384
|
-
|
399
|
+
other_color = create(other_color) unless other_color.is_a? Color
|
400
|
+
create(ThresholdFinder::Lightness.find(rgb, other_color.rgb, level))
|
385
401
|
end
|
386
402
|
|
387
403
|
##
|
@@ -522,7 +538,7 @@ module ColorContrastCalc
|
|
522
538
|
|
523
539
|
def generate_new_color(calc, ratio, name = nil)
|
524
540
|
new_rgb = calc.calc_rgb(rgb, ratio)
|
525
|
-
|
541
|
+
create(new_rgb, name)
|
526
542
|
end
|
527
543
|
|
528
544
|
private :generate_new_color
|
@@ -267,7 +267,7 @@ module ColorContrastCalc
|
|
267
267
|
# @return [true, false] return true when the opacity equals 1.0
|
268
268
|
|
269
269
|
def opaque?
|
270
|
-
opacity ==
|
270
|
+
opacity == Utils::MAX_OPACITY
|
271
271
|
end
|
272
272
|
|
273
273
|
# @private
|
@@ -335,6 +335,16 @@ module ColorContrastCalc
|
|
335
335
|
|
336
336
|
private_constant :MAX_SOURCE_LENGTH
|
337
337
|
|
338
|
+
def self.compose_error_message(scanner, message)
|
339
|
+
color_value = sanitized_source(scanner)
|
340
|
+
out = StringIO.new
|
341
|
+
out.print message
|
342
|
+
out.print ' '
|
343
|
+
ErrorReporter.print_error_pos!(out, color_value, scanner.charpos)
|
344
|
+
out.puts
|
345
|
+
out.string
|
346
|
+
end
|
347
|
+
|
338
348
|
def self.format_error_message(scanner, re)
|
339
349
|
out = StringIO.new
|
340
350
|
color_value = sanitized_source(scanner)
|
@@ -368,7 +378,7 @@ module ColorContrastCalc
|
|
368
378
|
|
369
379
|
class Parser
|
370
380
|
class << self
|
371
|
-
attr_accessor :parsers
|
381
|
+
attr_accessor :parsers, :main, :value, :function
|
372
382
|
end
|
373
383
|
|
374
384
|
def skip_spaces!(scanner)
|
@@ -449,7 +459,7 @@ module ColorContrastCalc
|
|
449
459
|
read_unit!(scanner, parsed_value)
|
450
460
|
end
|
451
461
|
|
452
|
-
|
462
|
+
protected :read_number!
|
453
463
|
|
454
464
|
def read_unit!(scanner, parsed_value)
|
455
465
|
unit = scanner.scan(TokenRe::UNIT)
|
@@ -463,15 +473,10 @@ module ColorContrastCalc
|
|
463
473
|
|
464
474
|
def read_separator!(scanner, parsed_value)
|
465
475
|
if next_spaces_as_separator?(scanner)
|
466
|
-
return read_number!(scanner, parsed_value)
|
476
|
+
return Parser.function.read_number!(scanner, parsed_value)
|
467
477
|
end
|
468
478
|
|
469
|
-
|
470
|
-
check_next_token(scanner, TokenRe::SLASH)
|
471
|
-
return read_opacity!(scanner, parsed_value)
|
472
|
-
end
|
473
|
-
|
474
|
-
read_comma!(scanner, parsed_value)
|
479
|
+
Parser.value.read_comma!(scanner, parsed_value)
|
475
480
|
end
|
476
481
|
|
477
482
|
private :read_separator!
|
@@ -496,11 +501,6 @@ module ColorContrastCalc
|
|
496
501
|
|
497
502
|
private :next_spaces_as_separator?
|
498
503
|
|
499
|
-
def read_opacity!(scanner, parsed_value)
|
500
|
-
read_token!(scanner, TokenRe::SLASH)
|
501
|
-
read_number!(scanner, parsed_value)
|
502
|
-
end
|
503
|
-
|
504
504
|
def read_comma!(scanner, parsed_value)
|
505
505
|
skip_spaces!(scanner)
|
506
506
|
|
@@ -510,10 +510,74 @@ module ColorContrastCalc
|
|
510
510
|
read_number!(scanner, parsed_value)
|
511
511
|
end
|
512
512
|
|
513
|
-
|
513
|
+
protected :read_comma!
|
514
|
+
end
|
515
|
+
|
516
|
+
class ValueParser < Parser
|
517
|
+
def read_separator!(scanner, parsed_value)
|
518
|
+
if next_spaces_as_separator?(scanner)
|
519
|
+
error_message = report_wrong_separator!(scanner, parsed_value)
|
520
|
+
raise InvalidColorRepresentationError, error_message
|
521
|
+
end
|
522
|
+
|
523
|
+
read_comma!(scanner, parsed_value)
|
524
|
+
end
|
525
|
+
|
526
|
+
def report_wrong_separator!(scanner, parsed_value)
|
527
|
+
scheme = parsed_value[:scheme].upcase
|
528
|
+
template = '" " and "," as a separator should not be used mixedly in %s functions.'
|
529
|
+
message = format(template, scheme)
|
530
|
+
ErrorReporter.compose_error_message(scanner, message)
|
531
|
+
end
|
532
|
+
|
533
|
+
private :report_wrong_separator!
|
514
534
|
end
|
515
535
|
|
516
536
|
class FunctionParser < Parser
|
537
|
+
def read_separator!(scanner, parsed_value)
|
538
|
+
if next_spaces_as_separator?(scanner)
|
539
|
+
error_if_opacity_separator_expected(scanner, parsed_value)
|
540
|
+
read_number!(scanner, parsed_value)
|
541
|
+
elsif opacity_separator_is_next?(scanner, parsed_value)
|
542
|
+
read_opacity!(scanner, parsed_value)
|
543
|
+
else
|
544
|
+
read_comma!(scanner, parsed_value)
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
private :read_separator!
|
549
|
+
|
550
|
+
def error_if_opacity_separator_expected(scanner, parsed_value)
|
551
|
+
return unless parsed_value[:parameters].length == 3
|
552
|
+
|
553
|
+
error_message = report_wrong_opacity_separator!(scanner, parsed_value)
|
554
|
+
raise InvalidColorRepresentationError, error_message
|
555
|
+
end
|
556
|
+
|
557
|
+
private :error_if_opacity_separator_expected
|
558
|
+
|
559
|
+
def report_wrong_opacity_separator!(scanner, parsed_value)
|
560
|
+
scheme = parsed_value[:scheme].upcase
|
561
|
+
message = "\"/\" is expected as a separator for opacity in #{scheme} functions."
|
562
|
+
ErrorReporter.compose_error_message(scanner, message)
|
563
|
+
end
|
564
|
+
|
565
|
+
private :report_wrong_opacity_separator!
|
566
|
+
|
567
|
+
def opacity_separator_is_next?(scanner, parsed_value)
|
568
|
+
parsed_value[:parameters].length == 3 &&
|
569
|
+
check_next_token(scanner, TokenRe::SLASH)
|
570
|
+
end
|
571
|
+
|
572
|
+
private :opacity_separator_is_next?
|
573
|
+
|
574
|
+
def read_opacity!(scanner, parsed_value)
|
575
|
+
read_token!(scanner, TokenRe::SLASH)
|
576
|
+
read_number!(scanner, parsed_value)
|
577
|
+
end
|
578
|
+
|
579
|
+
private :read_opacity!
|
580
|
+
|
517
581
|
def read_comma!(scanner, parsed_value)
|
518
582
|
skip_spaces!(scanner)
|
519
583
|
|
@@ -527,15 +591,9 @@ module ColorContrastCalc
|
|
527
591
|
end
|
528
592
|
|
529
593
|
def report_wrong_separator!(scanner, parsed_value)
|
530
|
-
out = StringIO.new
|
531
|
-
color_value = scanner.string
|
532
594
|
scheme = parsed_value[:scheme].upcase
|
533
|
-
|
534
|
-
|
535
|
-
out.print "\",\" is not a valid separator for #{scheme} functions. "
|
536
|
-
ErrorReporter.print_error_pos!(out, color_value, scanner.charpos)
|
537
|
-
out.puts
|
538
|
-
out.string
|
595
|
+
message = "\",\" is not a valid separator for #{scheme} functions."
|
596
|
+
ErrorReporter.compose_error_message(scanner, message)
|
539
597
|
end
|
540
598
|
|
541
599
|
private :report_wrong_separator!
|
@@ -552,7 +610,9 @@ module ColorContrastCalc
|
|
552
610
|
Scheme::HWB => FunctionParser.new
|
553
611
|
}
|
554
612
|
|
555
|
-
|
613
|
+
Parser.main = Parser.new
|
614
|
+
Parser.value = ValueParser.new
|
615
|
+
Parser.function = FunctionParser.new
|
556
616
|
|
557
617
|
##
|
558
618
|
# Parse an RGB/HSL/HWB function and store the result as an instance of
|
@@ -563,7 +623,7 @@ module ColorContrastCalc
|
|
563
623
|
# @return [ColorFunction] An instance of ColorFunctionParser::ColorFunction
|
564
624
|
|
565
625
|
def self.parse(color_value)
|
566
|
-
parsed_value =
|
626
|
+
parsed_value = Parser.main.read_scheme!(StringScanner.new(color_value))
|
567
627
|
ColorFunction.create(parsed_value, color_value)
|
568
628
|
end
|
569
629
|
|
@@ -7,6 +7,10 @@ module ColorContrastCalc
|
|
7
7
|
class InvalidColorRepresentationError < StandardError
|
8
8
|
module Template
|
9
9
|
RGB = 'An RGB value should be in form of [r, g, b], but %s.'
|
10
|
+
RGBA = <<~RGBA_MESSAGE
|
11
|
+
An RGB value should be in form of [r, g, b, opacity]
|
12
|
+
(r, g, b should be in the range between 0 and 255), but %s.
|
13
|
+
RGBA_MESSAGE
|
10
14
|
COLOR_NAME = '%s seems to be an undefined color name.'
|
11
15
|
HEX = 'A hex code #xxxxxx where 0 <= x <= f is expected, but %s.'
|
12
16
|
UNEXPECTED = 'A color should be given as an array or string, but %s.'
|
@@ -22,7 +26,7 @@ module ColorContrastCalc
|
|
22
26
|
def self.select_message_template(value)
|
23
27
|
case value
|
24
28
|
when Array
|
25
|
-
Template::RGB
|
29
|
+
value.length == 3 ? Template::RGB : Template::RGBA
|
26
30
|
when String
|
27
31
|
may_be_name?(value) ? Template::COLOR_NAME : Template::HEX
|
28
32
|
else
|
@@ -18,6 +18,7 @@ module ColorContrastCalc
|
|
18
18
|
module ColorComponent
|
19
19
|
RGB = 'rgb'.chars
|
20
20
|
HSL = 'hsl'.chars
|
21
|
+
HWB = 'hwb'.chars
|
21
22
|
end
|
22
23
|
|
23
24
|
module CompFunc
|
@@ -45,6 +46,9 @@ module ColorContrastCalc
|
|
45
46
|
# The function returned by Sorter.compile_compare_function() expects
|
46
47
|
# hex color codes as key values when this constants is specified.
|
47
48
|
HEX = :hex
|
49
|
+
# The function returned by Sorter.compile_compare_function() expects
|
50
|
+
# color functions as key values when this constants is specified.
|
51
|
+
FUNCTION = :function
|
48
52
|
# @private
|
49
53
|
CLASS_TO_TYPE = {
|
50
54
|
Color => COLOR,
|
@@ -62,14 +66,157 @@ module ColorContrastCalc
|
|
62
66
|
|
63
67
|
def self.guess(color, key_mapper = nil)
|
64
68
|
key = key_mapper ? key_mapper[color] : color
|
69
|
+
return FUNCTION if non_hex_code_string?(key)
|
65
70
|
CLASS_TO_TYPE[key.class]
|
66
71
|
end
|
72
|
+
|
73
|
+
def self.non_hex_code_string?(color)
|
74
|
+
color.is_a?(String) && !Utils.valid_hex?(color)
|
75
|
+
end
|
76
|
+
|
77
|
+
private_class_method :non_hex_code_string?
|
78
|
+
end
|
79
|
+
|
80
|
+
class CompareFunctionCompiler
|
81
|
+
def initialize(converters = nil)
|
82
|
+
@converters = converters
|
83
|
+
end
|
84
|
+
|
85
|
+
def compile(color_order)
|
86
|
+
order = parse_color_order(color_order)
|
87
|
+
create_proc(order, color_order)
|
88
|
+
end
|
89
|
+
|
90
|
+
# @private
|
91
|
+
|
92
|
+
def parse_color_order(color_order)
|
93
|
+
ordered_components = select_ordered_components(color_order)
|
94
|
+
pos = color_component_pos(color_order, ordered_components)
|
95
|
+
funcs = []
|
96
|
+
pos.each_with_index do |ci, i|
|
97
|
+
c = color_order[i]
|
98
|
+
funcs[ci] = Utils.uppercase?(c) ? CompFunc::DESCEND : CompFunc::ASCEND
|
99
|
+
end
|
100
|
+
{ pos: pos, funcs: funcs }
|
101
|
+
end
|
102
|
+
|
103
|
+
def select_ordered_components(color_order)
|
104
|
+
case color_order
|
105
|
+
when /[hsl]{3}/i
|
106
|
+
ColorComponent::HSL
|
107
|
+
when /[hwb]{3}/i
|
108
|
+
ColorComponent::HWB
|
109
|
+
else
|
110
|
+
ColorComponent::RGB
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private :select_ordered_components
|
115
|
+
|
116
|
+
# @private
|
117
|
+
|
118
|
+
def color_component_pos(color_order, ordered_components)
|
119
|
+
color_order.downcase.chars.map do |component|
|
120
|
+
ordered_components.index(component)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def create_proc(order, color_order)
|
125
|
+
if @converters
|
126
|
+
conv = select_converter(color_order)
|
127
|
+
proc {|color1, color2| compare(conv[color1], conv[color2], order) }
|
128
|
+
else
|
129
|
+
proc {|color1, color2| compare(color1, color2, order) }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private :create_proc
|
134
|
+
|
135
|
+
# @private
|
136
|
+
|
137
|
+
def compare_components(color1, color2, order)
|
138
|
+
funcs = order[:funcs]
|
139
|
+
order[:pos].each do |i|
|
140
|
+
result = funcs[i][color1[i], color2[i]]
|
141
|
+
return result unless result.zero?
|
142
|
+
end
|
143
|
+
|
144
|
+
0
|
145
|
+
end
|
146
|
+
|
147
|
+
alias compare compare_components
|
148
|
+
|
149
|
+
def select_converter(color_order)
|
150
|
+
scheme = select_scheme(color_order)
|
151
|
+
@converters[scheme]
|
152
|
+
end
|
153
|
+
|
154
|
+
private :select_converter
|
155
|
+
|
156
|
+
def select_scheme(color_order)
|
157
|
+
case color_order
|
158
|
+
when /[hsl]{3}/i
|
159
|
+
:hsl
|
160
|
+
when /[hwb]{3}/i
|
161
|
+
:hwb
|
162
|
+
else
|
163
|
+
:rgb
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
private :select_scheme
|
168
|
+
end
|
169
|
+
|
170
|
+
class CachingCompiler < CompareFunctionCompiler
|
171
|
+
def create_proc(order, color_order)
|
172
|
+
converter = select_converter(color_order)
|
173
|
+
cache = {}
|
174
|
+
|
175
|
+
proc do |color1, color2|
|
176
|
+
c1 = to_components(color1, converter, cache)
|
177
|
+
c2 = to_components(color2, converter, cache)
|
178
|
+
|
179
|
+
compare(c1, c2, order)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_components(color, converter, cache)
|
184
|
+
cached_components = cache[color]
|
185
|
+
return cached_components if cached_components
|
186
|
+
|
187
|
+
components = converter[color]
|
188
|
+
cache[color] = components
|
189
|
+
|
190
|
+
components
|
191
|
+
end
|
192
|
+
|
193
|
+
private :to_components
|
67
194
|
end
|
68
195
|
|
69
|
-
|
70
|
-
|
196
|
+
hex_to_components = {
|
197
|
+
# shorthands for Utils.hex_to_rgb() and .hex_to_hsl()
|
71
198
|
rgb: Utils.method(:hex_to_rgb),
|
72
|
-
hsl: Utils.method(:hex_to_hsl)
|
199
|
+
hsl: Utils.method(:hex_to_hsl),
|
200
|
+
hwb: Utils.method(:hex_to_hwb)
|
201
|
+
}
|
202
|
+
|
203
|
+
function_to_components = {
|
204
|
+
rgb: proc {|func| ColorContrastCalc.color_from(func).rgb },
|
205
|
+
hsl: proc {|func| ColorContrastCalc.color_from(func).hsl },
|
206
|
+
hwb: proc {|func| ColorContrastCalc.color_from(func).hwb }
|
207
|
+
}
|
208
|
+
|
209
|
+
color_to_components = {
|
210
|
+
rgb: proc {|color| color.rgb },
|
211
|
+
hsl: proc {|color| color.hsl },
|
212
|
+
hwb: proc {|color| color.hwb }
|
213
|
+
}
|
214
|
+
|
215
|
+
COMPARE_FUNCTION_COMPILERS = {
|
216
|
+
KeyTypes::COLOR => CompareFunctionCompiler.new(color_to_components),
|
217
|
+
KeyTypes::COMPONENTS => CompareFunctionCompiler.new,
|
218
|
+
KeyTypes::HEX => CachingCompiler.new(hex_to_components),
|
219
|
+
KeyTypes::FUNCTION => CachingCompiler.new(function_to_components)
|
73
220
|
}.freeze
|
74
221
|
|
75
222
|
##
|
@@ -114,14 +261,7 @@ module ColorContrastCalc
|
|
114
261
|
key_mapper = nil, &key_mapper_block)
|
115
262
|
key_mapper = key_mapper_block if !key_mapper && key_mapper_block
|
116
263
|
|
117
|
-
|
118
|
-
when KeyTypes::COLOR
|
119
|
-
compare = compile_color_compare_function(color_order)
|
120
|
-
when KeyTypes::COMPONENTS
|
121
|
-
compare = compile_components_compare_function(color_order)
|
122
|
-
when KeyTypes::HEX
|
123
|
-
compare = compile_hex_compare_function(color_order)
|
124
|
-
end
|
264
|
+
compare = COMPARE_FUNCTION_COMPILERS[key_type].compile(color_order)
|
125
265
|
|
126
266
|
compose_function(compare, key_mapper)
|
127
267
|
end
|
@@ -135,101 +275,5 @@ module ColorContrastCalc
|
|
135
275
|
compare_function[key_mapper[color1], key_mapper[color2]]
|
136
276
|
end
|
137
277
|
end
|
138
|
-
|
139
|
-
# @private
|
140
|
-
|
141
|
-
def self.color_component_pos(color_order, ordered_components)
|
142
|
-
color_order.downcase.chars.map do |component|
|
143
|
-
ordered_components.index(component)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
# @private
|
148
|
-
|
149
|
-
def self.parse_color_order(color_order)
|
150
|
-
ordered_components = ColorComponent::RGB
|
151
|
-
ordered_components = ColorComponent::HSL if hsl_order?(color_order)
|
152
|
-
pos = color_component_pos(color_order, ordered_components)
|
153
|
-
funcs = []
|
154
|
-
pos.each_with_index do |ci, i|
|
155
|
-
c = color_order[i]
|
156
|
-
funcs[ci] = Utils.uppercase?(c) ? CompFunc::DESCEND : CompFunc::ASCEND
|
157
|
-
end
|
158
|
-
{ pos: pos, funcs: funcs }
|
159
|
-
end
|
160
|
-
|
161
|
-
# @private
|
162
|
-
|
163
|
-
def self.hsl_order?(color_order)
|
164
|
-
/[hsl]{3}/i.match?(color_order)
|
165
|
-
end
|
166
|
-
|
167
|
-
# @private
|
168
|
-
|
169
|
-
def self.compare_color_components(color1, color2, order)
|
170
|
-
funcs = order[:funcs]
|
171
|
-
order[:pos].each do |i|
|
172
|
-
result = funcs[i][color1[i], color2[i]]
|
173
|
-
return result unless result.zero?
|
174
|
-
end
|
175
|
-
|
176
|
-
0
|
177
|
-
end
|
178
|
-
|
179
|
-
# @private
|
180
|
-
|
181
|
-
def self.compile_components_compare_function(color_order)
|
182
|
-
order = parse_color_order(color_order)
|
183
|
-
|
184
|
-
proc do |color1, color2|
|
185
|
-
compare_color_components(color1, color2, order)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
# @private
|
190
|
-
|
191
|
-
def self.compile_hex_compare_function(color_order)
|
192
|
-
order = parse_color_order(color_order)
|
193
|
-
converter = HEX_TO_COMPONENTS[:rgb]
|
194
|
-
converter = HEX_TO_COMPONENTS[:hsl] if hsl_order?(color_order)
|
195
|
-
cache = {}
|
196
|
-
|
197
|
-
proc do |hex1, hex2|
|
198
|
-
color1 = hex_to_components(hex1, converter, cache)
|
199
|
-
color2 = hex_to_components(hex2, converter, cache)
|
200
|
-
|
201
|
-
compare_color_components(color1, color2, order)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
# @private
|
206
|
-
|
207
|
-
def self.hex_to_components(hex, converter, cache)
|
208
|
-
cached_components = cache[hex]
|
209
|
-
return cached_components if cached_components
|
210
|
-
|
211
|
-
components = converter[hex]
|
212
|
-
cache[hex] = components
|
213
|
-
|
214
|
-
components
|
215
|
-
end
|
216
|
-
|
217
|
-
private_class_method :hex_to_components
|
218
|
-
|
219
|
-
# @private
|
220
|
-
|
221
|
-
def self.compile_color_compare_function(color_order)
|
222
|
-
order = parse_color_order(color_order)
|
223
|
-
|
224
|
-
if hsl_order?(color_order)
|
225
|
-
proc do |color1, color2|
|
226
|
-
compare_color_components(color1.hsl, color2.hsl, order)
|
227
|
-
end
|
228
|
-
else
|
229
|
-
proc do |color1, color2|
|
230
|
-
compare_color_components(color1.rgb, color2.rgb, order)
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
278
|
end
|
235
279
|
end
|
@@ -12,6 +12,9 @@ module ColorContrastCalc
|
|
12
12
|
module Utils
|
13
13
|
using Shim unless //.respond_to? :match?
|
14
14
|
|
15
|
+
MIN_OPACITY = 0
|
16
|
+
MAX_OPACITY = 1.0
|
17
|
+
|
15
18
|
HSL_UPPER_LIMIT = [360, 100, 100].freeze
|
16
19
|
|
17
20
|
private_constant :HSL_UPPER_LIMIT
|
@@ -196,11 +199,9 @@ module ColorContrastCalc
|
|
196
199
|
# @return [true, false] true if a valid HSL value is passed
|
197
200
|
|
198
201
|
def self.valid_hsl?(hsl)
|
199
|
-
|
200
|
-
|
201
|
-
return false if !c.is_a?(Numeric) || c < 0 || c > HSL_UPPER_LIMIT[i]
|
202
|
+
hsl.length == 3 && hsl.each_with_index.all? do |c, i|
|
203
|
+
c.is_a?(Numeric) && c >= 0 && c <= HSL_UPPER_LIMIT[i]
|
202
204
|
end
|
203
|
-
true
|
204
205
|
end
|
205
206
|
|
206
207
|
##
|
@@ -238,10 +239,10 @@ module ColorContrastCalc
|
|
238
239
|
end
|
239
240
|
|
240
241
|
module Hwb
|
241
|
-
|
242
|
-
# ref: https://www.w3.org/TR/2019/WD-css-color-4-20191105/
|
242
|
+
HWB_UPPER_LIMIT = [360, 100, 100].freeze
|
243
243
|
|
244
244
|
def normalize_hwb(hwb)
|
245
|
+
# https://www.w3.org/TR/2019/WD-css-color-4-20191105/
|
245
246
|
h, w, b = hwb
|
246
247
|
|
247
248
|
achromatic_percent = w + b
|
@@ -255,6 +256,12 @@ module ColorContrastCalc
|
|
255
256
|
|
256
257
|
private :normalize_hwb
|
257
258
|
|
259
|
+
##
|
260
|
+
# Convert an HWB value to an RGB value.
|
261
|
+
#
|
262
|
+
# @param hwb [Array<Float>] HWB value represented as an array of numbers
|
263
|
+
# @return [Array<Integer>] RGB value represented as an array of integers
|
264
|
+
|
258
265
|
def hwb_to_rgb(hwb)
|
259
266
|
hue, white, black = normalize_hwb(hwb)
|
260
267
|
rgb = Utils.hsl_to_rgb([hue, 100, 50])
|
@@ -264,8 +271,51 @@ module ColorContrastCalc
|
|
264
271
|
end
|
265
272
|
end
|
266
273
|
|
267
|
-
|
268
|
-
|
274
|
+
##
|
275
|
+
# Convert an HWB value to hex color code.
|
276
|
+
#
|
277
|
+
# @param hwb [Array<Float>] HWB value represented as an array of numbers
|
278
|
+
# @return [String] Hex color code such as "#ffff00"
|
279
|
+
|
280
|
+
def hwb_to_hex(hwb)
|
281
|
+
rgb_to_hex(hwb_to_rgb(hwb))
|
282
|
+
end
|
283
|
+
|
284
|
+
##
|
285
|
+
# Convert an RGB value to an HWB value.
|
286
|
+
#
|
287
|
+
# @param rgb [Array<Integer>] RGB value represented as an array of
|
288
|
+
# integers
|
289
|
+
# @return [Array<Float>] HWB value represented as an array of numbers
|
290
|
+
|
291
|
+
def rgb_to_hwb(rgb)
|
292
|
+
# https://www.w3.org/TR/2020/WD-css-color-4-20201112/
|
293
|
+
hsl = Utils.rgb_to_hsl(rgb)
|
294
|
+
white = rgb.min
|
295
|
+
black = 255 - rgb.max
|
296
|
+
[hsl[0], white * 100 / 255.0, black * 100 / 255.0]
|
297
|
+
end
|
298
|
+
|
299
|
+
##
|
300
|
+
# Convert hex color code to an HWB value.
|
301
|
+
#
|
302
|
+
# @param hex_code [String] Hex color code such as "#ffff00"
|
303
|
+
# @return [Array<Float>] HWB value represented as an array of numbers
|
304
|
+
|
305
|
+
def hex_to_hwb(hex_code)
|
306
|
+
rgb_to_hwb(Utils.hex_to_rgb(hex_code))
|
307
|
+
end
|
308
|
+
|
309
|
+
##
|
310
|
+
# Check if a given array is a valid representation of HWB color.
|
311
|
+
#
|
312
|
+
# @param hwb [Array<Float>] HWB value represented as an array of numbers
|
313
|
+
# @return [true, false] true if a valid HWB value is passed
|
314
|
+
|
315
|
+
def valid_hwb?(hwb)
|
316
|
+
hwb.length == 3 && hwb.each_with_index.all? do |c, i|
|
317
|
+
c.is_a?(Numeric) && c >= 0 && c <= HWB_UPPER_LIMIT[i]
|
318
|
+
end
|
269
319
|
end
|
270
320
|
end
|
271
321
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: color_contrast_calc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HASHIMOTO, Naoki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
19
|
+
version: '2.1'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '2.
|
26
|
+
version: '2.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,42 +44,42 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
47
|
+
version: '3.10'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
54
|
+
version: '3.10'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubocop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '1.7'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '1.7'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: yard
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.9
|
75
|
+
version: '0.9'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.9
|
82
|
+
version: '0.9'
|
83
83
|
description:
|
84
84
|
email:
|
85
85
|
- hashimoto.naoki@gmail.com
|
@@ -135,15 +135,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '2.
|
138
|
+
version: '2.4'
|
139
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
140
|
requirements:
|
141
141
|
- - ">="
|
142
142
|
- !ruby/object:Gem::Version
|
143
143
|
version: '0'
|
144
144
|
requirements: []
|
145
|
-
|
146
|
-
rubygems_version: 2.7.6
|
145
|
+
rubygems_version: 3.1.4
|
147
146
|
signing_key:
|
148
147
|
specification_version: 4
|
149
148
|
summary: Utility that helps you choose colors with sufficient contrast, WCAG 2.0 in
|