Skip to content

fix(svg): a gradient takes its stops, and what it does not set, from the gradient its href names - #481

Merged
sidorares merged 1 commit into
masterfrom
fix/svg-gradient-href
Sep 30, 2026
Merged

sidorares merged 1 commit into
masterfrom
fix/svg-gradient-href

Conversation

@sidorares

@sidorares sidorares commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

A gradient may name another with href or xlink:href, and takes from it
what it does not set itself, and its stops where it has none of its own, at
any depth (SVG 1.1, 13.2.2). An editor writes a drawing's colours once, as
a gradient of stops alone, and each gradient that uses them as a line with
a reference to it. SvgView read a gradient's own attributes and children
and nothing else: such a gradient had no stops, and what it filled was not
drawn. The crane down the left of the CSS Zen Garden's 219, a 59 KB drawing
every path of which is filled so, was missing whole.

A gradient is now read with the ones it takes after, each once, so two that
name each other end. gradientTransform is read too, which the same files
carry, in the bounding box's space where that is the gradient's units. A
linear gradient's line goes where the matrix takes its lines of one colour,
which is where the matrix takes its ends only while it keeps right angles.
A radial gradient's centre goes with the matrix and its radius with the
matrix's scale, and one a matrix stretches stays a circle, of the same
area, since a context's radial gradient is one.

Two tests hold the chain, a radial gradient taking a linear one's stops,
stops of its own winning and a loop, and a moved, a stretched and a turned
line and a scaled circle. Each fails without the part it is for.
docs/svg.md lists both.

…the gradient its href names

A gradient may name another with `href` or `xlink:href`, and takes from it
what it does not set itself, and its stops where it has none of its own, at
any depth (SVG 1.1, 13.2.2). An editor writes a drawing's colours once, as
a gradient of stops alone, and each gradient that uses them as a line with
a reference to it. `SvgView` read a gradient's own attributes and children
and nothing else: such a gradient had no stops, and what it filled was not
drawn. The crane down the left of the CSS Zen Garden's 219, a 59 KB drawing
every path of which is filled so, was missing whole.

A gradient is now read with the ones it takes after, each once, so two that
name each other end. `gradientTransform` is read too, which the same files
carry, in the bounding box's space where that is the gradient's units. A
linear gradient's line goes where the matrix takes its lines of one colour,
which is where the matrix takes its ends only while it keeps right angles.
A radial gradient's centre goes with the matrix and its radius with the
matrix's scale, and one a matrix stretches stays a circle, of the same
area, since a context's radial gradient is one.

Two tests hold the chain, a radial gradient taking a linear one's stops,
stops of its own winning and a loop, and a moved, a stretched and a turned
line and a scaled circle. Each fails without the part it is for.
docs/svg.md lists both.
@sidorares

Copy link
Copy Markdown
Owner Author

Two squares filled by gradients that take their stops from another, and the Zen Garden's 219 crane (bg1.svg), in Chrome, before and after:

shot-ntk-svg

@sidorares
sidorares merged commit 9db8108 into master Sep 30, 2026
6 checks passed
@sidorares
sidorares deleted the fix/svg-gradient-href branch September 30, 2026 10:15
sidorares pushed a commit that referenced this pull request Sep 30, 2026
🤖 I have created a release *beep* *boop*
---


## [8.17.2](v8.17.1...v8.17.2)
(2026-09-30)


### Bug Fixes

* **svg:** a gradient takes its stops, and what it does not set, from
the gradient its href names
([#481](#481))
([9db8108](9db8108))
* **text:** a font weight between two of the hundreds goes to fontconfig
on its own scale, so 450 in Arial is the regular and not the bold
([#480](#480))
([3973b42](3973b42))
* **text:** a variable font is instantiated out of a WOFF or a WOFF2,
where the first weight off the file's default threw
([#479](#479))
([b009cdd](b009cdd))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
sidorares added a commit that referenced this pull request Oct 1, 2026
…old, where it drew nothing (#497)

A `<switch>` renders the first of its child elements whose conditional
processing attributes hold, and none of the rest. `SvgView` had no case
for it, read it as a shape, found no geometry and drew nothing under it.
The CSS Zen Garden's 219 wraps the ring of three of its footer icons in
one, a single plain `<path>` inside a `<switch transform="matrix(…)">`,
and those icons came out as their inner glyph alone.

A switch is now a group of one: its own transform and opacity apply as a
`<g>`'s do, and it draws the first child that holds. That child is drawn
as it says, so one that is `display: none` is chosen and draws nothing,
as SVG 2 has it and browsers do.

The attributes are read on every element, as SVG 1.1, 5.8 asks, and the
paint scan walks the same way, so `paintKind` counts only the chosen
child. `requiredExtensions` never holds, since none is supported: that
is
the Illustrator export, its own data in a `foreignObject` behind Adobe's
extension and the drawing after it. `requiredFeatures` holds unless it
names a feature drawn nothing of here, `foreignObject` above all, so a
draw.io label draws as the `<text>` written after it for renderers
without one; browsers, which dropped the attribute, take the
`foreignObject`. `systemLanguage` matches a new `languages` option,
`navigator.languages` or the runtime's locale by default, a tag matching
another narrowed or widened by a subtag.

Gradients that take their stops through `xlink:href`, the other gap
found
on 219, were fixed in #481 and released in 8.17.3.

Five mock tests and a pixel test cover it, each failing without the fix.
docs/svg.md gains a section on conditional processing.

219's footer.svg at half size, master on the left, this branch on the
right, rendered headless into a pixmap through XQuartz:

<img width="572" alt="footer.svg before and after"
src="https://github.com/user-attachments/assets/403cae6b-9884-409b-b139-6be5fa166a8f"
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant