Background
Split-off from #266. PR #312/#314 closed the percent-only calc half of parseGradientStops. A separate follow-up PR closes the bg-position half via lazy resolution at draw time. This issue tracks the remaining gradient piece, which requires an architectural change rather than a localized parser fix.
Problem
parseLinearGradient and parseRadialGradient in html/converter_helpers.go (lines 550, 572) call RenderLinearGradient / RenderRadialGradient at parse time. The renderers bake the gradient into a fixed 200x200 bitmap (layout/gradient.go:52-74).
Because rasterization happens before layout, the gradient line length is unknown, so stop positions can only be expressed as fractions of an arbitrary canvas. The percent-only calc walker added in #312 covers the common case. Plain lengths (100px, 1em) and mixed-unit calc (calc(50% + 10px)) still drop to the default position.
What needs to change
To resolve stop positions against the actual rendered gradient line, rasterization must move from parse-time to render-time so it can receive the painting box dimensions.
Open design questions:
- Cache key: include resolved dimensions, or rasterize per unique container size?
- Sharing: gradients used at multiple sizes need multiple bitmaps; impact on PDF object dedup
- Stop-position type:
GradientStop.Position float64 becomes *cssLength (matches the BackgroundImage.Position change from the bg-position PR)
- Two-axis resolution: linear gradient stops resolve against the gradient line length, not the box width or height directly
Reproducer
background: linear-gradient(red, blue 100px, green);
Expected: blue stop 100pt from the start of the gradient line, green at end.
Actual: blue stop position drops to 0, gradient becomes blue-to-green from start.
Acceptance
- Plain-length stop positions (
100px, 1em, 2rem) resolve against the actual gradient line length
- Mixed-unit calc stop positions (
calc(50% + 10px)) resolve correctly
- Existing percent-only stops keep working without regression
- No visible quality loss on common gradient sizes (verify rasterization-at-actual-size doesn't introduce moire on small boxes)
Target: 0.9.0.
Background
Split-off from #266. PR #312/#314 closed the percent-only calc half of
parseGradientStops. A separate follow-up PR closes the bg-position half via lazy resolution at draw time. This issue tracks the remaining gradient piece, which requires an architectural change rather than a localized parser fix.Problem
parseLinearGradientandparseRadialGradientinhtml/converter_helpers.go(lines 550, 572) callRenderLinearGradient/RenderRadialGradientat parse time. The renderers bake the gradient into a fixed 200x200 bitmap (layout/gradient.go:52-74).Because rasterization happens before layout, the gradient line length is unknown, so stop positions can only be expressed as fractions of an arbitrary canvas. The percent-only calc walker added in #312 covers the common case. Plain lengths (
100px,1em) and mixed-unit calc (calc(50% + 10px)) still drop to the default position.What needs to change
To resolve stop positions against the actual rendered gradient line, rasterization must move from parse-time to render-time so it can receive the painting box dimensions.
Open design questions:
GradientStop.Position float64becomes*cssLength(matches theBackgroundImage.Positionchange from the bg-position PR)Reproducer
Expected: blue stop 100pt from the start of the gradient line, green at end.
Actual: blue stop position drops to 0, gradient becomes blue-to-green from start.
Acceptance
100px,1em,2rem) resolve against the actual gradient line lengthcalc(50% + 10px)) resolve correctlyTarget: 0.9.0.