6 Paint and Color
This group resolves SVG/CSS paint and color values —
The central function is parse-paint, which recognizes every common
CSS color syntax (#rgb, #rrggbb, rgb()/rgba(),
hsl()/hsla(), named colors, currentcolor) plus SVG’s
url(#id) paint-server references —
procedure
(parse-paint s [current-color])
→ (or/c (is-a?/c color%) paint-ref? #f) s : (or/c string? #f) current-color : (or/c (is-a?/c color%) #f) = #f
#f, for "none" or when s is itself #f;
a color%, for #rgb, #rrggbb, rgb(...)/rgba(...), hsl(...)/hsla(...), a named CSS color, or "currentcolor" (which resolves to current-color, or black if current-color is #f);
a paint-ref, for a "url(#id) fallback" reference —
resolving the reference itself (looking id up and building a gradient or pattern brush from it) is the caller’s job, since parse-paint has no document context to resolve it against. fallback, if the url(...) has a trailing color/keyword after it, is itself already fully parsed (i.e. a color%, #f, or another paint-ref, never a raw string).
Named colors use Racket’s own X11-derived color database, except for 16
CSS1/HTML4 keyword names confirmed to differ from what CSS/SVG actually
specify for the same name (most famously "purple", but also
"green", "gray", "maroon", and "navy")
—
url (...) is recognized whether its argument is unquoted
("url(#id)") or quoted ("url('#id')"/"url(\"#id\")"),
and only leading/trailing whitespace around the reference as a whole is
stripped —
(parse-paint "#ff0000") ; (is-a? color%), red (parse-paint "rgba(0, 0, 0, 0.5)") ; (is-a? color%), 50%-alpha black (parse-paint "none") ; #f (parse-paint "url(#grad) blue") ; (paint-ref "grad" (is-a? color%))
Four colors resolved by parse-paint —

struct
(struct paint-ref (id fallback) #:extra-constructor-name make-paint-ref) id : string? fallback : (or/c (is-a?/c color%) paint-ref? #f)
(paint-ref-id (parse-paint "url(#g) red")) ; "g" (paint-ref-fallback (parse-paint "url(#g) red")) ; (is-a? color%), red
procedure
(parse-opacity s [default]) → (real-in 0 1)
s : (or/c string? #f) default : (real-in 0 1) = 1
> (parse-opacity "0.5") 0.5
> (parse-opacity "50%") 0.5
> (parse-opacity "150%") 1.0
> (parse-opacity #f) 1
procedure
(resolve-gradient-stops node)
→ (listof (cons/c (real-in 0 1) (is-a?/c color%))) node : any/c
(resolve-gradient-stops '(linearGradient () (stop ((offset "0") (stop-color "red"))) (stop ((offset "1") (stop-color "blue") (stop-opacity "0.5"))))) ; (list (cons 0.0 (is-a? color%)) (cons 1.0 (is-a? color%)))
The three stops above, rendered as an actual gradient bar (gold at 0, orange-red at 0.5, purple at 60%-opacity at 1):

procedure
(combined-bounding-box paths)
→ (list/c real? real? real? real?) paths : (listof (is-a?/c dc-path%))
> (combined-bounding-box (path-data->dc-paths "M0,0 L50,0 L25,50 Z")) '(0.0 0.0 50.0 50.0)
The same curved shape from earlier, with its bounding box outlined:
