Conversation
9a6cc10 to
8d1f4a6
Compare
8d1f4a6 to
e1c597c
Compare
e1c597c to
f1d41e3
Compare
f1d41e3 to
019e30a
Compare
saecki
left a comment
There was a problem hiding this comment.
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
cf45a21 to
977c510
Compare
|
@saecki ready for 2nd review |
saecki
left a comment
There was a problem hiding this comment.
Thanks! And sorry for the delay (we had quite a busy week).
crates/typst-pdf/src/paint.rs
Outdated
| 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()) |
There was a problem hiding this comment.
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 🤔
There was a problem hiding this comment.
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 |
|---|---|---|
![]() |
![]() |
![]() |
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
rectto 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.

As it's a bit difficult to see, here a variant with a sharp gradient and different colors:

It's a design decision (at least until linear gradient start/end point can be controlled, see #2282).
Let me know what you think.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Okay, I applied the proposed changes and replaced the gradient-fill-and-stroke test
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.
|
@saecki ready for 3rd review
No worries. I look forward to watching the talks from the meeting on your YouTube channel. |



Fixes issues with gradient strokes for lines and curves:
curveelements don't use the correct bounding box when last segment iscurve.line#6068Tests 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
SVG
PDF
issue-5705-gradient-border-stroke paged




PNG
SVG
PDF
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
gradient-curve-stroke




PNG
SVG
PDF
gradient-curve-stroke-quad




PNG
SVG
PDF