diff --git a/src/clone-node.ts b/src/clone-node.ts index 5dfcd117..0833884a 100644 --- a/src/clone-node.ts +++ b/src/clone-node.ts @@ -133,6 +133,20 @@ function cloneCSSStyle( if (sourceStyle.cssText) { targetStyle.cssText = sourceStyle.cssText targetStyle.transformOrigin = sourceStyle.transformOrigin + // Re-apply background-image explicitly: some browsers normalise gradient + // stop-positions when round-tripping through cssText serialisation, which + // causes a repeating-linear-gradient to lose its repeating behaviour and + // render identically to a plain linear-gradient. Fetching the value via + // getPropertyValue bypasses the shorthand serialiser and returns the exact + // computed value as the browser resolved it. + const bgImage = sourceStyle.getPropertyValue('background-image') + if (bgImage && bgImage !== 'none') { + targetStyle.setProperty( + 'background-image', + bgImage, + sourceStyle.getPropertyPriority('background-image'), + ) + } } else { getStyleProperties(options).forEach((name) => { let value = sourceStyle.getPropertyValue(name) diff --git a/test/resources/repeating-gradient/node.html b/test/resources/repeating-gradient/node.html new file mode 100644 index 00000000..5113f722 --- /dev/null +++ b/test/resources/repeating-gradient/node.html @@ -0,0 +1,5 @@ +
diff --git a/test/resources/repeating-gradient/style.css b/test/resources/repeating-gradient/style.css new file mode 100644 index 00000000..f4516631 --- /dev/null +++ b/test/resources/repeating-gradient/style.css @@ -0,0 +1,13 @@ +/* Regression test for: repeating-linear-gradient treated as linear-gradient. + * The gradient tile is 20px (0 → 10px → 20px), which creates a visible stripe + * pattern. Without the fix the repeating- prefix may be lost during CSS cloning, + * causing the gradient to stretch across the full element height instead. */ +.gradient-box { + background-image: repeating-linear-gradient( + to bottom, + rgb(255, 0, 0) 0px, + rgb(255, 0, 0) 10px, + rgb(0, 0, 255) 10px, + rgb(0, 0, 255) 20px + ); +} diff --git a/test/spec/svg.spec.ts b/test/spec/svg.spec.ts index f4595a11..11c20463 100644 --- a/test/spec/svg.spec.ts +++ b/test/spec/svg.spec.ts @@ -2,6 +2,7 @@ import '../spec/setup' import { toSvg } from '../../src' +import { cloneNode } from '../../src/clone-node' import { bootstrap, renderAndCheck, getSvgDocument } from '../spec/helper' describe('work with svg element', () => { @@ -57,4 +58,27 @@ describe('work with svg element', () => { .then(done) .catch(done) }) + + it('should preserve repeating-linear-gradient in cloned element style', (done) => { + // Regression test for: repeating-linear-gradient treated as linear-gradient. + // The cssText serialisation path (Firefox/Safari) normalises gradient stop + // positions to 0%/100%, making the gradient visually indistinguishable from + // a plain linear-gradient. The fix explicitly re-applies background-image + // via getPropertyValue after cssText assignment. + bootstrap('repeating-gradient/node.html', 'repeating-gradient/style.css') + .then((node) => cloneNode(node, {}, true)) + .then((clonedNode) => { + expect(clonedNode).not.toBeNull() + const box = clonedNode!.querySelector( + '.gradient-box', + ) as HTMLElement | null + expect(box).not.toBeNull() + // The cloned element must have an inline background-image that preserves + // the repeating-linear-gradient function name (not normalised away). + const bgImage = box!.style.backgroundImage + expect(bgImage).toContain('repeating-linear-gradient') + }) + .then(done) + .catch(done) + }) })