diff --git a/src/clone-node.ts b/src/clone-node.ts index 500ce332..741b3034 100644 --- a/src/clone-node.ts +++ b/src/clone-node.ts @@ -133,11 +133,11 @@ function cloneCSSStyle(nativeNode: T, clonedNode: T) { ) { value = 'block' } - + if (name === 'd' && clonedNode.getAttribute('d')) { value = `path(${clonedNode.getAttribute('d')})` } - + targetStyle.setProperty( name, value, @@ -193,13 +193,45 @@ async function ensureSVGSymbols( const processedDefs: { [key: string]: HTMLElement } = {} for (let i = 0; i < uses.length; i++) { const use = uses[i] - const id = use.getAttribute('xlink:href') + const href = + use.getAttribute('href') ?? // check href first as this is preferred and will be used if both are set + use.getAttribute('xlink:href') + + if (!href) continue // skip blank hrefs + + const [svgUrl, id] = href.split('#') + if (id) { - const exist = clone.querySelector(id) - const definition = document.querySelector(id) as HTMLElement - if (!exist && definition && !processedDefs[id]) { + const query = `#${id}` + // const definition = ownerDocument?.querySelector(`svg ${ query }`) + const exist = clone.querySelector(query) + const definition = document.querySelector(query) as HTMLElement + + if (svgUrl) { + // change the href attribute to use a local symbol on this cloned use-node + use.setAttribute('href', query) + // No need to set xlink:href since this is ignored when href is set + } + + if (exist || processedDefs[query]) continue // already exists in defs + + if (definition) { + // found local embedded definition // eslint-disable-next-line no-await-in-loop processedDefs[id] = (await cloneNode(definition, options, true))! + } else if (svgUrl) { + // no local definition but found an url + // try to fetch the svg and append it to the svgDefsElement + try { + // wrapped in try/catch since this is network calls that is likely to fail + // eslint-disable-next-line no-await-in-loop + const response = await fetch(svgUrl) + // eslint-disable-next-line no-await-in-loop + const svgData = await response.text() + clone.insertAdjacentHTML('beforeend', svgData) + } catch (error) { + // TODO log errors here? + } } } } diff --git a/test/resources/svg-use-tag-external/image b/test/resources/svg-use-tag-external/image new file mode 100644 index 00000000..5e5f4054 --- /dev/null +++ b/test/resources/svg-use-tag-external/image @@ -0,0 +1 @@ +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAwpJREFUeF7tmPGNzFEYRe90QAWUoAN0QAXogApQAR2gAjpAB3RABzpY+TZvZCLDnkl+bkb2vGT+mdzMfXPOvt/3ZncXyUVcZ0NgN0J2ye5sdnSNN3LpQiHn8xegkPNxcbkTIuRekudJXib5lORxkkdJPid5cWbf57/fzlVCBv6bg2/5JMntJWiEjCzXhgT+JuTtOgnv1kmY0zAn40eSG+uEKGRDGX96ZA3sj0nuJJkTMdBHxAj6muTV2oMnZGMZx4SMhPdJbq5ZMbNj3tuvL2uWjJz57XI/ybzn2ojA4SPr4ZoX35O8XidhTsvv69uS8jTJrXWKPmy0n2v/MYdC5hY1j6eBOyfjmIw9sJkjk3+w8iPQtQGBY0N9rrZ3wWc7QwCkUyNEyLM1J2aW7Af69CjkVNogT4TM4J5TM4+zuX3tl0IA4FMjCjmV2D/OHxMyv87ntV9zm5qr7TyyDof3XH3n5dqQwFX/Otmwyo8iBBRCKBUzCinCJlUKIZSKGYUUYZMqhRBKxYxCirBJlUIIpWJGIUXYpEohhFIxo5AibFKlEEKpmFFIETapUgihVMwopAibVCmEUCpmFFKETaoUQigVMwopwiZVCiGUihmFFGGTKoUQSsWMQoqwSZVCCKViRiFF2KRKIYRSMaOQImxSpRBCqZhRSBE2qVIIoVTMKKQIm1QphFAqZhRShE2qFEIoFTMKKcImVQohlIoZhRRhkyqFEErFjEKKsEmVQgilYkYhRdikSiGEUjGjkCJsUqUQQqmYUUgRNqlSCKFUzCikCJtUKYRQKmYUUoRNqhRCKBUzCinCJlUKIZSKGYUUYZMqhRBKxYxCirBJlUIIpWJGIUXYpEohhFIxo5AibFKlEEKpmFFIETapUgihVMwopAibVCmEUCpmFFKETaoUQigVMwopwiZVCiGUihmFFGGTKoUQSsWMQoqwSZVCCKViRiFF2KRKIYRSMaOQImxSpRBCqZhRSBE2qVIIoVTMKKQIm1QphFAqZhRShE2qFEIoFTMKKcImVb+EkLCZDoGfbnm/rNO8R/AAAAAASUVORK5CYII= diff --git a/test/resources/svg-use-tag-external/node.html b/test/resources/svg-use-tag-external/node.html new file mode 100644 index 00000000..b49f12ef --- /dev/null +++ b/test/resources/svg-use-tag-external/node.html @@ -0,0 +1,9 @@ +
+
+ + + +
+
diff --git a/test/resources/svg-use-tag-external/sprites.svg b/test/resources/svg-use-tag-external/sprites.svg new file mode 100644 index 00000000..0cbd2768 --- /dev/null +++ b/test/resources/svg-use-tag-external/sprites.svg @@ -0,0 +1,15 @@ + + + + home + + + + diff --git a/test/resources/svg-use-tag-external/style.css b/test/resources/svg-use-tag-external/style.css new file mode 100644 index 00000000..b51cf808 --- /dev/null +++ b/test/resources/svg-use-tag-external/style.css @@ -0,0 +1,27 @@ +#dom-node { + width: 100px; + overflow: hidden; +} + +#root { + border: 1px solid red; + position: relative; + height: 100px; +} + +svg { + width: 100%; + height: 100%; +} + +.icon { + display: inline-block; + width: 0.9285714285714285em; + height: 1em; + stroke-width: 0; + stroke: currentColor; + fill: currentColor; + vertical-align: middle; + top: -1px; + position: relative; +} diff --git a/test/spec/helper.ts b/test/spec/helper.ts index 03e8a432..7f49c33e 100644 --- a/test/spec/helper.ts +++ b/test/spec/helper.ts @@ -154,7 +154,7 @@ export function compareToRefImage(sourceData: ImageData, threshold = 0.1) { pixelmatch(sourceData.data, refData.data, null, ref.width, ref.height, { threshold, }), - ).toBeLessThan(100) + ).toBeLessThan(2) // Temporarily changed this threshold from 100 to 2 to so that failing tests actually fail } export async function getSvgDocument(dataUrl: string): Promise { diff --git a/test/spec/svg.spec.ts b/test/spec/svg.spec.ts index f4595a11..9e005d53 100644 --- a/test/spec/svg.spec.ts +++ b/test/spec/svg.spec.ts @@ -47,7 +47,7 @@ describe('work with svg element', () => { .catch(done) }) - it('should render SVG use tags', function (done) { + it('should render embedded SVG use tags', function (done) { bootstrap( 'svg-use-tag/node.html', 'svg-use-tag/style.css', @@ -57,4 +57,15 @@ describe('work with svg element', () => { .then(done) .catch(done) }) + + it('should render external SVG use tags', function (done) { + bootstrap( + 'svg-use-tag-external/node.html', + 'svg-use-tag-external/style.css', + 'svg-use-tag-external/image', + ) + .then(renderAndCheck) + .then(done) + .catch(done) + }) })