Skip to content

Fix gradient stroke for line and curve#7863

Open
eltos wants to merge 13 commits intotypst:mainfrom
eltos:eltos-fix-gradient-line-stroke
Open

Fix gradient stroke for line and curve#7863
eltos wants to merge 13 commits intotypst:mainfrom
eltos:eltos-fix-gradient-line-stroke

Conversation

@eltos
Copy link
Contributor

@eltos eltos commented Feb 13, 2026

Fixes issues with gradient strokes for lines and curves:

Tests added

Left / right shows before / after this PR. PDF screenshots are from Adobe Acrobat (report.html does not render them correctly with my firefox)

issue-6068-curve-stroke-gradient
PNG
grafik
SVG
canvas
PDF
canvascanvas

issue-5705-gradient-border-stroke paged
PNG

SVG

PDF
2026-02-16 19-34-00

In the test images below, each "star" consists of lines/curves at different angles, all using the same stroke gradient.

gradient-line-stroke
PNG

SVG

PDF
grafik

gradient-curve-stroke
PNG

SVG

PDF
canvas

gradient-curve-stroke-quad
PNG

SVG

PDF
canvas

@eltos eltos force-pushed the eltos-fix-gradient-line-stroke branch from 9a6cc10 to 8d1f4a6 Compare February 14, 2026 09:33
@laurmaedje laurmaedje added the waiting-on-author Pull request waits on author label Feb 16, 2026
@eltos eltos force-pushed the eltos-fix-gradient-line-stroke branch from 8d1f4a6 to e1c597c Compare February 16, 2026 17:10
@eltos eltos force-pushed the eltos-fix-gradient-line-stroke branch from e1c597c to f1d41e3 Compare February 16, 2026 18:59
@eltos eltos force-pushed the eltos-fix-gradient-line-stroke branch from f1d41e3 to 019e30a Compare February 16, 2026 19:17
@eltos eltos marked this pull request as ready for review February 16, 2026 19:25
@eltos eltos changed the title Fix gradient stroke for line Fix gradient stroke for line and curve Feb 17, 2026
@laurmaedje laurmaedje added waiting-on-review This PR is waiting to be reviewed. and removed waiting-on-author Pull request waits on author labels Feb 18, 2026
@laurmaedje laurmaedje requested a review from saecki February 18, 2026 10:13
@laurmaedje laurmaedje added visualize Related to the visualize category, which is about drawing and illustrating fix A bug fix. labels Feb 18, 2026
Copy link
Member

@saecki saecki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for your work on gradients! This looks like some great improvements.

I agree with closing #5647 as expected behavior.

I think you're not part of the discord server, but since you're quite involved with gradients I'd be interested in your opinion regarding conic gradients. I've opened an issue just now to discuss the design: #7890

@eltos eltos force-pushed the eltos-fix-gradient-line-stroke branch from cf45a21 to 977c510 Compare February 20, 2026 18:44
@eltos
Copy link
Contributor Author

eltos commented Feb 20, 2026

@saecki ready for 2nd review

Copy link
Member

@saecki saecki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! And sorry for the delay (we had quite a busy week).

Geometry::Line(_) | Geometry::Curve(_) => s.geometry.bbox(s.stroke.as_ref()),
// Special handling for fill of rectangles (mirrors gradients for negative sizes)
Geometry::Rect(_) => {
Rect::from_pos_size(Point::zero(), s.geometry.bbox_size())
Copy link
Member

@saecki saecki Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just noticed that by not passing the stroke, the bbox will be wrong for rectangles created by the typst_layout::shapes::simple_rect function such as:

#rect(
  width: 100pt,
  height: 100pt,
  stroke: gradient.linear(green, black, yellow, angle: 90deg).sharp(8) + 20pt,
)

The gradient is not spreading to the edge of the stroke (the weird clipping in the converted PDF is an issue that should be fixed in a newer hayro version)
report.html

So essentially the special handling for rectangles also has to check if the paint is converted for the stroke or the fill of the rectangle shape.

EDIT: It's probably desirable to have the same transform as the rect fill here, so let's just leave this as is 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it again, and I think for consistency it would actually be better to use different bboxes, i.e. use the tight bbox-without-stroke for the fill gradient, and the extended bbox-with-stroke for the stroke gradient.
Here's an example with a rect, rect(radius: 20pt), curve and circle:

Before PR Current state of PR Proposed
before fc70e77 proposed

Reasoning:

  • Before PR: uses bbox-without-stroke for both fill and stroke, this breaks gradients on lines, curves etc. where the bbox size collapses
  • Current state of PR: Uses bbox-with-stroke for strokes on lines and curves but also changes bbox for some fills. This leads to inconsistency because of the special handling for rects. For round rects the situation is more complex as fill and strokes are handled separately and the codepath that generates the fill is not aware of the strokes (I think the reason being that strokes can be different for the four edges). So using the bbox-with-strokes for the fill is currently not possible.
  • Proposed: Keep using the bbox-without-stroke for fills as before, and use bbox-with-stroke for all strokes (even for rect to handle zero-sized rects with strokes). This results in different bboxes for fill and stroke, but is consistent across shapes, predictable and backwards compatible with respect to gradient fills.

Note that the test "gradient-fill-and-stroke", which with the current state of the PR still passes, would change with the proposed version.
canvas
As it's a bit difficult to see, here a variant with a sharp gradient and different colors:
2026-03-07 11-32-27

It's a design decision (at least until linear gradient start/end point can be controlled, see #2282).
Let me know what you think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the test "gradient-fill-and-stroke", which with the current state of the PR still passes, would change with the proposed version.

That's what I thought of when I wanted to dismiss this comment, but I didn't think of the other cases where this already happens because the rectangle is decomposed into different shapes.

I thought about it again, and I think for consistency it would actually be better to use different bboxes, i.e. use the tight bbox-without-stroke for the fill gradient, and the extended bbox-with-stroke for the stroke gradient.

I agree, we definitely either want the state Before PR or the Proposed one, not the in-between we currently have. Since the Before PR state wouldn't allow properly and consistently dealing with gradients on strokes, I think it's better to opt for your proposed changes.

Maybe the use case of gradient-fill-and-stroke can be revisited together with relative gradients and tilings at some point. But at least for the example shown, just removing the stroke and using an outset should be enough to replicate it.

Copy link
Contributor Author

@eltos eltos Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I applied the proposed changes and replaced the gradient-fill-and-stroke test

2026-03-09 17-11-39

Also did some more refactoring, including what you suggested earlier:

Then we could get rid of the bbox_size, and [...]
Finally we could add a Shape::bbox() method, which uses the shape's stroke field, since that seems to always be passed anyway.
This refactor can be done in a follow-up PR, it just feels like this would remove a lot of the complications.

@laurmaedje laurmaedje added waiting-on-author Pull request waits on author and removed waiting-on-review This PR is waiting to be reviewed. labels Mar 3, 2026
@eltos
Copy link
Contributor Author

eltos commented Mar 6, 2026

@saecki ready for 3rd review

Thanks! And sorry for the delay (we had quite a busy week).

No worries. I look forward to watching the talks from the meeting on your YouTube channel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix A bug fix. visualize Related to the visualize category, which is about drawing and illustrating waiting-on-author Pull request waits on author

Projects

None yet

3 participants