diff --git a/src/shape/svg/svg_import.js b/src/shape/svg/svg_import.js index 5f363c8b54..b0ed21c138 100644 --- a/src/shape/svg/svg_import.js +++ b/src/shape/svg/svg_import.js @@ -699,12 +699,26 @@ export function SVGImportAddon(p5, fn, lifecycles) { ? this.num(node, "height") : (referenced.height?.baseVal?.value || vb.height); - const scale = Math.min(w / vb.width, h / vb.height); // default: xMidYMid meet - this.tStack.current.translateSelf( - (w - vb.width * scale) / 2 - vb.x * scale, - (h - vb.height * scale) / 2 - vb.y * scale - ); - this.tStack.current.scaleSelf(scale, scale); + const preserveAspectRatio = referenced.getAttribute("preserveAspectRatio"); + + if (preserveAspectRatio?.trim() === "none") { + const scaleX = w / vb.width; + const scaleY = h / vb.height; + + this.tStack.current.translateSelf( + -vb.x * scaleX, + -vb.y * scaleY + ); + this.tStack.current.scaleSelf(scaleX, scaleY); + } else { + const scale = Math.min(w / vb.width, h / vb.height); // default: xMidYMid meet + + this.tStack.current.translateSelf( + (w - vb.width * scale) / 2 - vb.x * scale, + (h - vb.height * scale) / 2 - vb.y * scale + ); + this.tStack.current.scaleSelf(scale, scale); + } } this.visit(referenced); }); @@ -1623,4 +1637,4 @@ export function SVGImportAddon(p5, fn, lifecycles) { if (typeof p5 !== 'undefined') { p5.registerAddon(SVGImportAddon); -} \ No newline at end of file +} diff --git a/test/unit/assets/svg/defs-symbol-preserve-aspect-ratio-none.svg b/test/unit/assets/svg/defs-symbol-preserve-aspect-ratio-none.svg new file mode 100644 index 0000000000..363babf96a --- /dev/null +++ b/test/unit/assets/svg/defs-symbol-preserve-aspect-ratio-none.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/test/unit/svg/svg_import.js b/test/unit/svg/svg_import.js index a0545b9e5e..d4acf1d594 100644 --- a/test/unit/svg/svg_import.js +++ b/test/unit/svg/svg_import.js @@ -1134,6 +1134,43 @@ suite(' and elements', function () { }); }); + test('use respects preserveAspectRatio=none', function () { + const record = createSVG(` + + + + + + + + + `); + const scope = firstChild(record); + const node = scope.children[0]; + assert.closeTo(node.state.transform.a, 2, 0.001); + assert.closeTo(node.state.transform.d, 1, 0.001); + }); + + test('use with preserveAspectRatio=none accounts for viewBox origin', function () { + const record = createSVG(` + + + + + + + + + `); + const scope = firstChild(record); + const node = scope.children[0]; + + assert.closeTo(node.state.transform.a, 2, 0.001); + assert.closeTo(node.state.transform.d, 1, 0.001); + assert.closeTo(node.state.transform.e, -20, 0.001); + assert.closeTo(node.state.transform.f, -20, 0.001); + }); + test('handles missing references gracefully', function () { const record = createSVG(` diff --git a/test/unit/visual/cases/svg.js b/test/unit/visual/cases/svg.js index 82312cef9a..4a8d615a6b 100644 --- a/test/unit/visual/cases/svg.js +++ b/test/unit/visual/cases/svg.js @@ -713,6 +713,16 @@ visualSuite('svg', function () { await screenshot(); }); + visualTest('symbol preserveAspectRatio none', async (p, screenshot) => { + setupDefault(p); + const shape = await loadFixture( + p, + 'defs-symbol-preserve-aspect-ratio-none' + ); + p.shape(shape); + await screenshot(); + }); + visualTest('x and y', async (p, screenshot) => { setupDefault(p); const shape = await loadFixture(p, 'defs-use-xy'); diff --git a/test/unit/visual/screenshots/svg/SVG Import - defs and use/symbol preserveAspectRatio none/000.png b/test/unit/visual/screenshots/svg/SVG Import - defs and use/symbol preserveAspectRatio none/000.png new file mode 100644 index 0000000000..7cb512e0ad Binary files /dev/null and b/test/unit/visual/screenshots/svg/SVG Import - defs and use/symbol preserveAspectRatio none/000.png differ diff --git a/test/unit/visual/screenshots/svg/SVG Import - defs and use/symbol preserveAspectRatio none/metadata.json b/test/unit/visual/screenshots/svg/SVG Import - defs and use/symbol preserveAspectRatio none/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/svg/SVG Import - defs and use/symbol preserveAspectRatio none/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file