Drawings and Shapes
Drawings
read-svg returns a drawing:
drawing-widthanddrawing-heightare its size: itswidthandheightattributes in px, the view box's size where they are left out, or 100 by 100 without either.drawing-shapesis a simple vector of its shapes in paint order, the first under the rest. Shapes of groups and ofuseelements are in their place; what SVG does not render where it is, such as the contents ofdefsor what hasdisplay: none, is not there.drawing-view-boxis the view box as given, four single-floats#(min-x min-y width height), ornil. The shapes' transforms already map it onto the drawing, so it is there to read, not to apply.
The drawing covers 0, 0 to its width and height, in px. What lies outside is not part of the drawing, and a renderer clips it away.
Shapes
Each path element and basic shape becomes a shape:
| Reader | What it is |
|---|---|
shape-path |
Its path, in the shape's own coordinates. |
shape-transform |
The transform from the shape's coordinates to the drawing's: its own transform, its groups', and the view box's. |
shape-fill, shape-stroke |
Its paints: nil for none, a colour, or :current-color. |
shape-fill-opacity, shape-stroke-opacity |
From 0 to 1, multiplying the alpha of the paint's colour. |
shape-fill-rule |
:nonzero or :even-odd. |
shape-stroke-width |
The stroke's width, in the shape's own coordinates. |
shape-stroke-line-join |
:miter, :miter-clip, :round or :bevel. |
shape-stroke-line-cap |
:butt, :round or :square. |
shape-stroke-miter-limit |
The ratio of a miter's length to the stroke's width, at least 1. |
shape-paint-order |
:fill-stroke, or :stroke-fill where paint-order puts the stroke under the fill. |
shape-id |
The element's id, or nil. |
(shape-bounds shape) returns the box around what the shape paints, in the drawing's coordinates, its stroke included, as four values xmin ymin xmax ymax. The box holds the shape, though it may be larger than the shape is: it allows for the longest miter the miter limit allows. read-svg leaves out a shape that reaches beyond 10^30 from the drawing's origin, so the box always fits single-floats.
A shape whose stroke is nil draws no stroke, whatever its width, and a stroke of no width is nil.
Paints and colours
A colour is four single-floats from 0 to 1, red, green, blue and alpha, in sRGB with straight alpha, not premultiplied, as SVG gives colours. Opacity stays apart from colours: the alpha of a colour is its own, and the shape's opacities multiply it.
(cl-svgread:read-svg "<svg><g opacity='0.5'>
<rect width='4' height='4' fill='red' fill-opacity='0.5'/>
</g></svg>")
;; Its one shape: shape-fill => #(1.0 0.0 0.0 1.0), shape-fill-opacity => 0.25
shape-fill-opacity is the product of the shape's fill-opacity and opacity and of the opacity of every group around it; shape-stroke-opacity likewise with stroke-opacity. The product is exact for a shape painted with a fill or a stroke alone; see Reading SVG for where SVG would fade a layer as a whole.
currentColor
currentColor inherits as itself and takes the colour of the shape it paints, as CSS Color 4 has it. Where an element sets the color property, the shape's paint is that colour:
(cl-svgread:read-svg "<svg><g color='#ff0000'>
<rect width='4' height='4' fill='currentColor'/>
</g></svg>")
;; Its one shape: shape-fill => #(1.0 0.0 0.0 1.0)
Where no element sets it, the paint is :current-color, and whoever draws the shape chooses the colour: icons are drawn this way, so that a theme colours them. Rasterizing takes the colour as :current-color, opaque black by default.
Transforms and the view box
A transform is six single-floats #(a b c d e f), in the order of SVG's matrix(a b c d e f), which maps a point x, y to a x + c y + e, b x + d y + f. That is also the order of cl-gmath's mat3x2: column by column, the x axis, the y axis and the origin.
shape-transform includes the view box's mapping, so a shape's points, transformed, land in the drawing's px:
(let* ((drawing (cl-svgread:read-svg
"<svg width='32' height='32' viewBox='0 0 16 16'>
<rect x='2' y='2' width='4' height='4' transform='translate(1 0)'/>
</svg>"))
(shape (aref (cl-svgread:drawing-shapes drawing) 0)))
(list (cl-svgread:drawing-width drawing)
(cl-svgread:drawing-view-box drawing)
(cl-svgread:shape-transform shape)))
;; => (32.0 #(0.0 0.0 16.0 16.0) #(2.0 0.0 0.0 2.0 2.0 0.0))
The view box doubles everything, and the translate(1 0) becomes 2 px.
Strokes belong in the shape's coordinates
Stroke a shape's path in its own coordinates, with shape-stroke-width, then transform the stroke's outline with shape-transform. A transform that stretches one way more than the other then stretches the stroke with it, as SVG draws it. Rasterizing does this, at the image's resolution.
Threads
A drawing, its shapes and their paths never change once read, and may be shared between threads. Do not modify the arrays they return.
Using cl-svgread
Reference