I'm experimenting with taking an image of an existing element. First I copy the image to a foreignObject inside SVG element, then serialize it and use it as a "data" image source. But for some reason in Chrome I see the element rendered a bit differently. Does anybody know why this is the case and what can be done to make it right?
My HTML is this:
<!DOCTYPE html>
<html>
<head>
<style>
html,
body {
margin: 0
}
.x {
border: 1px solid black;
float: left;
width: 100.3px;
}
</style>
<script>
function test() {
let x = document.getElementById('xx')
let i = document.getElementById('if')
i.contentDocument.body.appendChild(document.getElementById('sv').cloneNode(true))
let c = i.contentDocument
let s = c.getElementById('sv')
let t = c.getElementById('st')
let f = c.getElementById('fo')
i.width = x.offsetWidth * 2
i.height = x.offsetHeight
s.viewBox.baseVal.width = x.offsetWidth
s.viewBox.baseVal.height = x.offsetHeight
s.width.baseVal.value = x.offsetWidth
s.height.baseVal.value = x.offsetHeight
f.width.baseVal.value = x.offsetWidth
f.height.baseVal.value = x.offsetHeight
t.textContent = document.getElementsByTagName('STYLE')[0].textContent
f.appendChild(x.cloneNode(true))
d = new XMLSerializer().serializeToString(s)
g = c.createElement('IMG')
g.width = x.offsetWidth
g.height = x.offsetHeight
g.src = 'data:image/svg+xml,' + encodeURIComponent(d)
c.body.appendChild(g)
}
</script>
</head>
<body onclick="test()">
<div class="x" id="xx">
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
</ul>
</div>
<svg xmlns="http://www.w3.org/2000/svg" id="sv" viewBox="0 0 100 100" width="100px" height="100px">
<style id="st"></style>
<foreignObject id="fo" width="100px" height="100px" style="background:lightgray"></foreignObject>
</svg>
<iframe id="if"></iframe>
</body>
</html>
Here is the result (after clicking somewhere in the body):
The list on the left is the original, then there is the blueprint I use for SVG, then the iframe with two elements in it: one is an SVG element with a foreignObject inside, showing the same list, and another is the IMG element that has the very same SVG, serialized, as its source. And for some reason they render a little bit differently in Chrome (it is fine in Firefox).
OK, so, it seems there is indeed a workaround. It throws things into disarray in Safari, so, I don't have a nice cross-platform code, but the issue is that HTML is rendered a bit differently depending on window.devicePixelRatio
. For images, it is always 1. But for the main content it was 2 (retina display).
The workaround is to first copy the content in SVG as it is here, but wrap that SVG in a DIV with zoom = 1/devicePixelRatio
; then adjust all the dimensions based on the cloned content's actual offsetHeight
and offsetWidth
, and only then do XML serialization and the rest.