Skip to content

Commit

Permalink
Merge pull request #1473 from Omikhleia/1440-color-parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
alerque committed Jul 20, 2022
2 parents a00cd64 + f2d7fac commit 6100740
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 190 deletions.
213 changes: 213 additions & 0 deletions core/color.lua
@@ -0,0 +1,213 @@
local colornames = {
aliceblue = { 240, 248, 255 },
antiquewhite = { 250, 235, 215 },
aqua = { 0, 255, 255 },
aquamarine = { 127, 255, 212 },
azure = { 240, 255, 255 },
beige = { 245, 245, 220 },
bisque = { 255, 228, 196 },
black = { 0, 0, 0 },
blanchedalmond = { 255, 235, 205 },
blue = { 0, 0, 255 },
blueviolet = { 138, 43, 226 },
brown = { 165, 42, 42 },
burlywood = { 222, 184, 135 },
cadetblue = { 95, 158, 160 },
chartreuse = { 127, 255, 0 },
chocolate = { 210, 105, 30 },
coral = { 255, 127, 80 },
cornflowerblue = { 100, 149, 237 },
cornsilk = { 255, 248, 220 },
crimson = { 220, 20, 60 },
cyan = { 0, 255, 255 },
darkblue = { 0, 0, 139 },
darkcyan = { 0, 139, 139 },
darkgoldenrod = { 184, 134, 11 },
darkgray = { 169, 169, 169 },
darkgreen = { 0, 100, 0 },
darkgrey = { 169, 169, 169 },
darkkhaki = { 189, 183, 107 },
darkmagenta = { 139, 0, 139 },
darkolivegreen = { 85, 107, 47 },
darkorange = { 255, 140, 0 },
darkorchid = { 153, 50, 204 },
darkred = { 139, 0, 0 },
darksalmon = { 233, 150, 122 },
darkseagreen = { 143, 188, 143 },
darkslateblue = { 72, 61, 139 },
darkslategray = { 47, 79, 79 },
darkslategrey = { 47, 79, 79 },
darkturquoise = { 0, 206, 209 },
darkviolet = { 148, 0, 211 },
deeppink = { 255, 20, 147 },
deepskyblue = { 0, 191, 255 },
dimgray = { 105, 105, 105 },
dimgrey = { 105, 105, 105 },
dodgerblue = { 30, 144, 255 },
firebrick = { 178, 34, 34 },
floralwhite = { 255, 250, 240 },
forestgreen = { 34, 139, 34 },
fuchsia = { 255, 0, 255 },
gainsboro = { 220, 220, 220 },
ghostwhite = { 248, 248, 255 },
gold = { 255, 215, 0 },
goldenrod = { 218, 165, 32 },
gray = { 128, 128, 128 },
grey = { 128, 128, 128 },
green = { 0, 128, 0 },
greenyellow = { 173, 255, 47 },
honeydew = { 240, 255, 240 },
hotpink = { 255, 105, 180 },
indianred = { 205, 92, 92 },
indigo = { 75, 0, 130 },
ivory = { 255, 255, 240 },
khaki = { 240, 230, 140 },
lavender = { 230, 230, 250 },
lavenderblush = { 255, 240, 245 },
lawngreen = { 124, 252, 0 },
lemonchiffon = { 255, 250, 205 },
lightblue = { 173, 216, 230 },
lightcoral = { 240, 128, 128 },
lightcyan = { 224, 255, 255 },
lightgoldenrodyellow = { 250, 250, 210 },
lightgray = { 211, 211, 211 },
lightgreen = { 144, 238, 144 },
lightgrey = { 211, 211, 211 },
lightpink = { 255, 182, 193 },
lightsalmon = { 255, 160, 122 },
lightseagreen = { 32, 178, 170 },
lightskyblue = { 135, 206, 250 },
lightslategray = { 119, 136, 153 },
lightslategrey = { 119, 136, 153 },
lightsteelblue = { 176, 196, 222 },
lightyellow = { 255, 255, 224 },
lime = { 0, 255, 0 },
limegreen = { 50, 205, 50 },
linen = { 250, 240, 230 },
magenta = { 255, 0, 255 },
maroon = { 128, 0, 0 },
mediumaquamarine = { 102, 205, 170 },
mediumblue = { 0, 0, 205 },
mediumorchid = { 186, 85, 211 },
mediumpurple = { 147, 112, 219 },
mediumseagreen = { 60, 179, 113 },
mediumslateblue = { 123, 104, 238 },
mediumspringgreen = { 0, 250, 154 },
mediumturquoise = { 72, 209, 204 },
mediumvioletred = { 199, 21, 133 },
midnightblue = { 25, 25, 112 },
mintcream = { 245, 255, 250 },
mistyrose = { 255, 228, 225 },
moccasin = { 255, 228, 181 },
navajowhite = { 255, 222, 173 },
navy = { 0, 0, 128 },
oldlace = { 253, 245, 230 },
olive = { 128, 128, 0 },
olivedrab = { 107, 142, 35 },
orange = { 255, 165, 0 },
orangered = { 255, 69, 0 },
orchid = { 218, 112, 214 },
palegoldenrod = { 238, 232, 170 },
palegreen = { 152, 251, 152 },
paleturquoise = { 175, 238, 238 },
palevioletred = { 219, 112, 147 },
papayawhip = { 255, 239, 213 },
peachpuff = { 255, 218, 185 },
peru = { 205, 133, 63 },
pink = { 255, 192, 203 },
plum = { 221, 160, 221 },
powderblue = { 176, 224, 230 },
purple = { 128, 0, 128 },
rebeccapurple = { 102, 51, 153 },
red = { 255, 0, 0 },
rosybrown = { 188, 143, 143 },
royalblue = { 65, 105, 225 },
saddlebrown = { 139, 69, 19 },
salmon = { 250, 128, 114 },
sandybrown = { 244, 164, 96 },
seagreen = { 46, 139, 87 },
seashell = { 255, 245, 238 },
sienna = { 160, 82, 45 },
silver = { 192, 192, 192 },
skyblue = { 135, 206, 235 },
slateblue = { 106, 90, 205 },
slategray = { 112, 128, 144 },
slategrey = { 112, 128, 144 },
snow = { 255, 250, 250 },
springgreen = { 0, 255, 127 },
steelblue = { 70, 130, 180 },
tan = { 210, 180, 140 },
teal = { 0, 128, 128 },
thistle = { 216, 191, 216 },
tomato = { 255, 99, 71 },
turquoise = { 64, 224, 208 },
violet = { 238, 130, 238 },
wheat = { 245, 222, 179 },
white = { 255, 255, 255 },
whitesmoke = { 245, 245, 245 },
yellow = { 255, 255, 0 },
yellowgreen = { 154, 205, 50 }
}

local color = pl.class({
type = "color",
-- Default values amount to much "darkness in the void"
r = nil,
g = nil,
b = nil,
c = nil,
m = nil,
y = nil,
k = nil,
l = nil,
})

function color:_init (input)
local c = self:parse(input)
for k, v in pairs(c) do
self[k] = v
end
return self
end

function color.parse (_, input)
local r, g, b, c, m, y, k, l
if not input or type(input) ~= "string" then
SU.error("Not a color specification string (" .. tostring(input) .. ")")
end
local named = colornames[string.lower(input)]
if named then
return { r = named[1] / 255, g = named[2] / 255, b = named[3] / 255 }
end
r, g, b = input:match("^#(%x%x)(%x%x)(%x%x)$")
if r then
return { r = tonumber("0x"..r)/255, g = tonumber("0x"..g)/255, b = tonumber("0x"..b)/255 }
end
r, g, b = input:match("^#(%x)(%x)(%x)$")
if r then
return { r = tonumber("0x"..r)/15, g = tonumber("0x"..g)/15, b = tonumber("0x"..b)/15 }
end
c, m, y, k = input:match("^(%d+%.?%d*)%s+(%d+%.?%d*)%s+(%d+%.?%d*)%s+(%d+%.?%d*)$")
if c then
return { c = tonumber(c)/255, m = tonumber(m)/255, y = tonumber(y)/255, k = tonumber(k)/255 }
end
c, m, y, k = input:match("^(%d+%.?%d*)%%%s+(%d+%.?%d*)%%%s+(%d+%.?%d*)%%%s+(%d+%.?%d*)%%$")
if c then
return { c = tonumber(c)/100, m = tonumber(m)/100, y = tonumber(y)/100, k = tonumber(k)/100 }
end
r, g, b = input:match("^(%d+%.?%d*)%s+(%d+%.?%d*)%s+(%d+%.?%d*)$")
if r then
return { r = tonumber(r)/255, g = tonumber(g)/255, b = tonumber(b)/255 }
end
r, g, b = input:match("^(%d+%.?%d*)%%%s+(%d+%.?%d*)%%%s+(%d+%.?%d*)%%$")
if r then
return { r = tonumber(r)/100, g = tonumber(g)/100, b = tonumber(b)/100 }
end
l = input:match("^(%d+.?%d*)$")
if l then
return { l = tonumber(l)/255 }
end
SU.error("Unparsable color " .. input)
end

return color
43 changes: 43 additions & 0 deletions core/color_spec.lua
@@ -0,0 +1,43 @@
SILE = require("core.sile")
SU.warn = function () end

describe("The color parser", function()
local parse = SILE.color
local rebecca = {
b = 0.6,
g = 0.2,
r = 0.4
}
local reddish = {
c = 0,
k = 0.3,
m = 0.81,
y = 0.81
}
it("should return the correct RGB values for a named color", function()
assert.is.same(parse("rebeccapurple"), rebecca)
assert.is.same(parse("RebeccaPurple"), rebecca)
end)
it("should return the correct RGB values for a hexadecimal specification", function()
assert.is.same(parse("#663399"), rebecca)
assert.is.same(parse("#639"), rebecca)
end)
it("should return the correct RGB values for a series of 3 numbers or percentages", function()
assert.is.same(parse("102 51 153"), rebecca)
assert.is.same(parse("40% 20% 60%"), rebecca)
end)
it("should return the correct CMYK values for a series of 4 numbers or percentages", function()
assert.is.same(parse("0% 81% 81% 30%"), reddish)
assert.is.same(parse("0 206.55 206.55 76.5"), reddish)
end)
it("should return the correct grayscale value for single number", function()
assert.is.same(parse("204"), { l = 0.8 })
end)

it("error if unable to parse", function()
assert.has.errors(function () parse("not_a_color") end)
assert.has.errors(function () parse(nil) end)
assert.has.errors(function () parse("") end)
end)
end)

0 comments on commit 6100740

Please sign in to comment.