Module: Colors::CSS

Defined in:
lib/colors/css.rb

Class Method Summary collapse

Class Method Details

.parse(css_string) ⇒ Object

Factory method for generating RGB/RGBA/HSL/HSLA Objects. Parsing based on spec https://www.w3.org/TR/css-color-3 ; section 4.2



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/colors/css.rb', line 5

def self.parse(css_string)
  error_message = "must be a string of `rgb(rr,gg,bb)`, `rgba(rr,gg,bb,aa)`, `hsl(hh,ss,ll)`, or `hsla(hh,ss,ll,aa)` form"
  unless css_string.respond_to?(:to_str)
    raise ArgumentError, "#{error_message}: #{css_string.inspect}"
  end

  css_string = css_string.to_str.strip

  matcher = /\A((?:rgb|hsl)a?)\(([^)]+)\)\z/.match(css_string)
  unless matcher
    raise ArgumentError, "#{error_message}: #{css_string.inspect}"
  end

  color_space, args_string = matcher[1..2]
  args = args_string.strip.split(/\s*,\s*/)
  has_alpha = color_space.end_with?("a") # rgba/hsla
  if has_alpha && args.length != 4
    raise ArgumentError, "Expecting 4 fields for #{color_space}(): #{css_string.inspect}"
  elsif !has_alpha && args.length != 3
    raise ArgumentError, "Expecting 3 fields for #{color_space}(): #{css_string.inspect}"
  end

  case color_space
  when "rgb", "rgba"
    rgb, alpha = args[0, 3], args[3]
    num_percent_vals = rgb.count {|v| v.end_with?("%") }
    # CSS3 allows RGB values to be specified with all 3 as a percentage "##%"
    # or all as integer values without '%' sign.
    if num_percent_vals.zero?
      # interpret as integer values in range of 0..255
      r, g, b = rgb.map {|strval| strval.to_i.clamp(0, 255) }
      # Note, RGBA.new expects all values to be integers or floats.
      # For this case, we also turn the alpha-value into an int range 0..255
      # to match the r,g,b values.
      a = has_alpha && (alpha.to_r.clamp(0r, 1r) * 255).to_i
    elsif num_percent_vals == 3
      r, g, b = rgb.map {|strval| (strval.to_r / 100).clamp(0r, 1r) }
      a = has_alpha && alpha.to_r.clamp(0r, 1r)
    else
      raise ArgumentError, "Invalid mix of percent and integer values: #{css_string.inspect}"
    end
    return has_alpha ? RGBA.new(r, g, b, a) : RGB.new(r, g, b)
  when "hsl", "hsla"
    hue, sat, light, alpha = *args
    # CSS3 Hue values are an angle, unclear if we should convert to Integer or Rational here.
    h = hue.to_r
    s, l = [sat, light].map {|strval| (strval.to_r / 100).clamp(0r, 1r) }
    if has_alpha
      a = alpha.to_r.clamp(0r, 1r)
      return HSLA.new(h, s, l, a)
    else
      return HSL.new(h, s, l)
    end
  else
    raise ArgumentError, "Unknown color space: #{css_string.inspect}"
  end
end