= Micro SVG Document Structure :toc: == Intro SVG Micro represents a strip down SVG Full 1.1 subset. Here is the main differences between SVG Full and SVG Micro. - No XML DTD. - No CSS. - `use`, `marker` and nested `svg` will be resolved. - Simplified path notation. Only absolute MoveTo, LineTo, CurveTo and ClosePath segments are allowed. - No inheritable attributes. - No `xlink:href`, except the `image` element. - No recursive references. - Only valid elements and attributes. - No unused elements. - No redundant groups. - No units. - No `objectBoundingBox` units. - No `viewBox` and `preserveAspectRatio` attributes. - No `style` attribute, except for `mix-blend-mode` and `isolation` - Default attributes are implicit. You can use https://github.com/RazrFalcon/resvg/tree/master/crates/usvg[usvg] to convert a random SVG into a SVG Micro almost losslessly. == Elements [[svg-element]] === The `svg` element The `svg` element is the root element of the document. It's defined only once and can't be nested, unlike by the SVG spec. *Children:* * <> * <> * <> * <> *Attributes:* * `width` = < >> + The width of the rectangular region into which the referenced document is placed. * `height` = < >> + The height of the rectangular region into which the referenced document is placed. [[defs-element]] === The `defs` element Always present. Always the first `svg` child. Can be empty. *Children:* * <> * <> * <> * <> * <> * <> * <> * <> * <> *Attributes:* * none [[linearGradient-element]] === The `linearGradient` element Doesn't have a `xlink:href` attribute because all attributes and `stop` children will be resolved. *Children:* * At least two <> *Attributes:* * `id` = < >> + The element ID. Always set. Guarantee to be unique. * `x1` = < >> * `y1` = < >> * `x2` = < >> * `y2` = < >> * `gradientUnits` = `userSpaceOnUse`? * `spreadMethod` = `reflect | repeat`? * `gradientTransform` = < >>? [[radialGradient-element]] === The `radialGradient` element Doesn't have a `xlink:href` attribute because all attributes and `stop` children will be resolved. *Children:* * At least two <> *Attributes:* * `id` = < >> + The element ID. Always set. Guarantee to be unique. * `cx` = < >> * `cy` = < >> * `fx` = < >> + Guarantee to be the circle defined by `cx`, `cy` and `r`. * `fy` = < >> + Guarantee to be inside the circle defined by `cx`, `cy` and `r`. * `r` = < >> * `gradientUnits` = `userSpaceOnUse` * `spreadMethod` = `reflect | repeat`? * `gradientTransform` = < >>? [[stop-element]] === The `stop` element Gradient's `stop` children will always have unique, ordered `offset` values in the 0..1 range. *Children:* * none *Attributes:* * `offset` = < >> * `stop-color` = < >> * `stop-opacity` = < >>? + Default: 1 [[pattern-element]] === The `pattern` element Doesn't have a `xlink:href` attribute because all attributes and children will be resolved. *Children:* * `g` * `path` * `image` *Attributes:* * `id` = < >> + The element ID. Always set. Guarantee to be unique. * `x` = < >> * `y` = < >> * `width` = < >> * `height` = < >> * `patternUnits` = `userSpaceOnUse` * `patternTransform` = < >>? [[clipPath-element]] === The `clipPath` element *Children:* * `path` *Attributes:* * `id` = < >> + The element ID. Always set. Guarantee to be unique. * `clip-path` = < >>? + An optional reference to a supplemental `clipPath`. + Default: none * `transform` = < >>? [[mask-element]] === The `mask` element *Children:* * `g` * `path` * `image` *Attributes:* * `id` = < >> + The element ID. Always set. Guarantee to be unique. * `mask` = < >>? + An optional reference to a supplemental `mask`. + Default: none * `x` = < >> * `y` = < >> * `width` = < >> * `height` = < >> * `mask-type` = `alpha`? + Default: luminance * `maskUnits` = `userSpaceOnUse` [[filter-element]] === The `filter` element Doesn't have a `xlink:href` attribute because all attributes and children will be resolved. *Children:* * <> *Attributes:* * `id` = < >> + The element ID. Always set. Guarantee to be unique. * `x` = < >> * `y` = < >> * `width` = < >> * `height` = < >> * `filterUnits` = `userSpaceOnUse` [[g-element]] === The `g` element The group element indicates that a new canvas should be created. All group's children elements will be rendered on it and then merged into the parent canvas. Since it's pretty expensive, especially memory wise, _usvg_ will remove as many groups as possible. And all the remaining one will indicate that a new canvas must be created. A group can have no children when it has a `filter` attribute. A group will have at least one of the attributes present. *Children:* * <> * <> * <> *Attributes:* * `id` = < >>? + An optional, but never empty, element ID. * `opacity` = < >>? * `clip-path` = < >>? + Cannot be set to `none`. * `mask` = < >>? + Cannot be set to `none`. * `filter` = < >>+ + Cannot be set to `none`. * `transform` = < >>? * `style` = < >>? + This is the only place where the `style` attribute is used. For reasons unknown, `mix-blend-mode` and `isolation` properties must not be set as attributes, only as part of the `style` attribute. + The set attribute will look like `mix-blend-mode:screen;isolation:isolate`. Both properties are always set. + The attribute is not present only in case of `mix-blend-mode:norma;isolation:auto` [[path-element]] === The `path` element *Children:* * none *Attributes:* * `id` = < >>? + An optional, but never empty, element ID. * `d` = < >> + * `fill` = `none` | < >> | < >> + If set to `none` than all fill-* attributes will not be set too. + Default: black * `fill-opacity` = < >>? + Default: 1 * `fill-rule` = `evenodd`? + Default: nonzero * `stroke` = `none` | < >> | < >> + If set to `none` than all stroke-* attributes will not be set too. + Default: none * `stroke-width` = < >>? + Default: 1 * `stroke-linecap` = `round | square`? + Default: butt * `stroke-linejoin` = `round | bevel`? + Default: miter * `stroke-miterlimit` = < >>? + Guarantee to be > 1. + Default: 4 * `stroke-dasharray` = ``? + Guarantee to have even amount of numbers. + Default: none * `stroke-dashoffset` = < >>? * `stroke-opacity` = < >>? + Default: 1 * `paint-order` = `normal | stroke`? + Default: `normal` + Only `stroke` will be written. * `clip-rule` = `evenodd`? + Will be set only inside the <>, instead of `fill-rule`. * `clip-path` = < >>? + Available only inside the <>. * `shape-rendering` = `optimizeSpeed | crispEdges`? + Default: geometricPrecision * `visibility` = `hidden`? + Default: visible * `transform` = < >>? + Can only be set on paths inside of `clipPath`. [[image-element]] === The `image` element *Children:* * none *Attributes:* * `id` = < >>? + An optional, but never empty, element ID. * `xlink:href` = < >> + The IRI contains a base64 encoded image. * `width` = < >> * `height` = < >> * `image-rendering` = `optimizeSpeed`? + Default: optimizeQuality * `visibility` = `hidden`? + Default: visible == Filter primitives === Filter primitive attributes The attributes below are the same for all filter primitives. * `color-interpolation-filters` = `sRGB`? + Default: linearRGB * `x` = < >>? * `y` = < >>? * `width` = < >>? * `height` = < >>? * `result` = < >> The `x`, `y`, `width` and `height` attributes can be omitted. SVG has a pretty complex https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion[rules of resolving them] and I don't fully understand them yet. Neither do others, because they are pretty poorly implemented. === Filter primitive `feBlend` *Attributes:* * `in` = < >> * `in2` = < >> * `mode` = `normal | multiply | screen | overlay | darken | lighten | color-dodge |color-burn | hard-light | soft-light | difference | exclusion | hue | saturation | color | luminosity` * <> === Filter primitive `feColorMatrix` *Attributes:* * `in` = < >> * `type` = `matrix | saturate | hueRotate | luminanceToAlpha` * `values` = ``? + ** For `type=matrix`, contains 20 numbers. ** For `type=saturate`, contains a single number in a 0..1 range. ** For `type=hueRotate`, contains a single number. ** Not present for `type=luminanceToAlpha`. * <> === Filter primitive `feComponentTransfer` *Children:* * `feFuncR` * `feFuncG` * `feFuncB` * `feFuncA` The all four will always be present. *Attributes:* * `in` = < >> * <> *`feFunc(R|G|B|A)` attributes:* * `type` = `identity | table | discrete | linear | gamma` * `tableValues` = ``? + Present only when `type=table | discrete`. Can be empty. * `slope` = < >>? + Present only when `type=linear`. * `intercept` = < >>? + Present only when `type=linear`. * `amplitude` = < >>? + Present only when `type=gamma`. * `exponent` = < >>? + Present only when `type=gamma`. * `offset` = < >>? + Present only when `type=gamma`. === Filter primitive `feComposite` *Attributes:* * `in` = < >> * `in2` = < >> * `operator` = `over | in | out | atop | xor | arithmetic` * `k1` = < >>? + Present only when `operator=arithmetic`. * `k2` = < >>? + Present only when `operator=arithmetic`. * `k3` = < >>? + Present only when `operator=arithmetic`. * `k4` = < >>? + Present only when `operator=arithmetic`. * <> === Filter primitive `feConvolveMatrix` *Attributes:* * `in` = < >> * `order` = < >> " " < >> + Both numbers are never 0. * `kernelMatrix` = `` * `divisor` = < >> + Never 0. * `bias` = < >> * `targetX` = < >> + Always smaller than the number of columns in the matrix. * `targetY` = < >> + Always smaller than the number of rows in the matrix. * `edgeMode` = `none | duplicate | wrap` * `preserveAlpha` = `true | false` * <> === Filter primitive `feDiffuseLighting` *Children:* Only one of: * `feDistantLight` * `fePointLight` * `feSpotLight` *Attributes:* * `in` = < >> * `surfaceScale` = < >> * `diffuseConstant` = < >> * `lighting-color` = < >> * <> `feDistantLight` *attributes:* * `azimuth` = < >> * `elevation` = < >> `fePointLight` *attributes:* * `x` = < >> * `y` = < >> * `z` = < >> `feSpotLight` *attributes:* * `x` = < >> * `y` = < >> * `z` = < >> * `pointsAtX` = < >> * `pointsAtY` = < >> * `pointsAtZ` = < >> * `specularExponent` = < >> * `limitingConeAngle` = < >>? === Filter primitive `feDisplacementMap` *Attributes:* * `in` = < >> * `in2` = < >> * `scale` = < >> * `xChannelSelector` = `R | G | B | A` * `yChannelSelector` = `R | G | B | A` * <> === Filter primitive `feDropShadow` *Attributes:* * `in` = < >> * `stdDeviation` = < >> " " < >> * `dx` = < >> * `dy` = < >> * `flood-color` = < >> * `flood-opacity` = < >> * <> === Filter primitive `feFlood` *Attributes:* * `flood-color` = < >> * `flood-opacity` = < >> * <> === Filter primitive `feGaussianBlur` *Attributes:* * `in` = < >> * `stdDeviation` = < >> " " < >> * <> === Filter primitive `feImage` *Attributes:* * `xlink:href` = < >> + The IRI contains a link to an element (like `use`). base64 encoded is not allowed and will be represented as a link to an `image`. * <> === Filter primitive `feMerge` *Children:* * `feMergeNode` *Attributes:* * <> *`feMergeNode` attributes:* * `in` = < >> === Filter primitive `feMorphology` *Attributes:* * `in` = < >> * `operator` = `erode | dilate` * `radius` = < >> " " < >> * <> === Filter primitive `feOffset` *Attributes:* * `in` = < >> * `dx` = < >> * `dy` = < >> * <> === Filter primitive `feSpecularLighting` *Children:* Only one of: * `feDistantLight` * `fePointLight` * `feSpotLight` *Attributes:* * `in` = < >> * `surfaceScale` = < >> * `specularConstant` = < >> * `specularExponent` = < >> + Number in a 1..128 range. * `lighting-color` = < >> * <> `feDistantLight` *attributes:* * `azimuth` = < >> * `elevation` = < >> `fePointLight` *attributes:* * `x` = < >> * `y` = < >> * `z` = < >> `feSpotLight` *attributes:* * `x` = < >> * `y` = < >> * `z` = < >> * `pointsAtX` = < >> * `pointsAtY` = < >> * `pointsAtZ` = < >> * `specularExponent` = < >> * `limitingConeAngle` = < >>? === Filter primitive `feTile` *Attributes:* * `in` = < >> * <> === Filter primitive `feTurbulence` *Attributes:* * `baseFrequency` = < >> " " < >> * `numOctaves` = < >> * `seed` = < >> * `stitchTiles` = `stitch | noStitch` * `type` = `fractalNoise | turbulence` * <> == Data types If an attribute has the `?` symbol after the type that's mean that that this attribute is optional. [[string-type]] ** - A Unicode (UTF-8) string. [[number-type]] ** - A real number. + `number ::= [-]? [0-9]+ "." [0-9]+` [[positive-number-type]] ** - A positive real <>. + `positive-number ::= [0-9]+ "." [0-9]+` [[integer-type]] ** - An integer. + `integer ::= [-]? [0-9]+` [[positive-integer-type]] ** - A positive integer. + `positive-integer ::= [0-9]+` [[opacity-type]] ** - A real <> in a 0..1 range. + `opacity ::= positive-number` [[offset-type]] ** - A real <> in a 0..1 range. + `offset ::= positive-number` [[color-type]] ** - A hex-encoded RGB color. ``` color ::= "#" hexdigit hexdigit hexdigit hexdigit hexdigit hexdigit hexdigit ::= [0-9a-f] ``` [[iri-type]] ** - An Internationalized Resource Identifier. Always a valid, local reference. + `IRI ::= string` [[func-iri-type]] ** - Functional notation for an <>. Always a valid, local reference. + `FuncIRI ::= url( )` [[filter-input-type]] ** - A filter source. A reference to a _result_ guarantee to be valid. ``` filter-input ::= SourceGraphic | SourceAlpha | ``` We do not support `FillPaint`, `StrokePaint`, `BackgroundImage` and `BackgroundAlpha`. [[transform-type]] ** - A transformation matrix. Always a `matrix` and not `translate`, `scale`, etc. Numbers are space-separated. + `transform ::= matrix( " " " " " " " " " " )` [[path-data-type]] ** - A path data. * Contains only absolute MoveTo, LineTo, CurveTo and ClosePath segments. * All segments are explicit. * The first segment is guarantee to be MoveTo. * Segments, commands and coordinates are separated only by space. * Path and all subpaths are guarantee to have at least two segments. Grammar: ``` svg-path: moveto-drawto-command-groups moveto-drawto-command-groups: moveto-drawto-command-group | moveto-drawto-command-group " " moveto-drawto-command-groups moveto-drawto-command-group: moveto " " drawto-commands drawto-commands: drawto-command | drawto-command " " drawto-commands drawto-command: closepath | lineto | curveto moveto: "M " coordinate-pair lineto: "L " coordinate-pair curveto: "C " coordinate-pair " " coordinate-pair " " coordinate-pair closepath: "Z" coordinate-pair: coordinate " " coordinate coordinate: sign? digit-sequence "." digit-sequence sign: "-" digit-sequence: digit | digit digit-sequence digit: "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ``` Basically, a path looks like this: `M 10.5 20 L 30 40`. Commands and numbers are separated by a space. Numbers with an exponent are not allowed. Trimmed numbers like `-.5` are not allowed.