Canvas toDataUrl在最新的safari上没有返回正确的图像



在浏览器中将SVG转换为图像(JPEG、PNG等)

它在chrome中工作良好,但在最新的safari更新15之后,生成的图像不正确。

这是chrome图像https://pastebin.com/hcLAS51W

这是safari生成的图像https://pastebin.com/S7tEWQsQ

const base64Image = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjkyLjk3NzUyODA4OTg4NzY0IDEyOSAzNzQuMDQ0OTQzODIwMjI0NyAxMTAiIGNsYXNzPSJQU1BERktpdC04czFzYmM1MTN0MzRxMzg4OTViNGJoM2p2biIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS10ZXN0aWQ9Imluay1zdmciIHN0eWxlPSJsZWZ0OiAwcHg7IHRvcDogMHB4OyB3aWR0aDogMTAwJTsgaGVpZ2h0OiAxMDAlOyBvcGFjaXR5OiAxOyBvdmVyZmxvdzogaGlkZGVuOyI+PHJlY3QgeD0iOTcuOTc3NTI4MDg5ODg3NjQiIHk9IjEzNCIgd2lkdGg9IjM2NC4wNDQ5NDM4MjAyMjQ3IiBoZWlnaHQ9IjEwMCIgY2xhc3M9IlBTUERGS2l0LTc4NzFyZmh5eGI3cGRta3MxbjN4N2F2dnRhIiBzdHlsZT0iZmlsbDogdHJhbnNwYXJlbnQ7Ii8+PGcgc3R5bGU9ImZpbGw6IHRyYW5zcGFyZW50OyI+PGc+PHBhdGggY2xhc3M9IlBTUERGS2l0LTQ3cjV6eHB0eWtoanFwenFkdWU5YWNwaHR6IFBTUERGS2l0LTQ0OG5jcjVtdWhycGM4cDh5dmFkeTUxZW1jIFBTUERGS2l0LVNtb290aC1MaW5lcyIgZD0iTSAyMTIuNzYzODUwODAyMDQ4MiwxNjYuMDQwNTk0NDE4MjY3NDYgQyAxODMuNiwxNjAuNyAxNTQuNSwxNTUuNCAxMzUuNiwxNTIuMyBDIDExNi43LDE0OS4xIDEwNy45LDE0OC4yIDEwNC4wLDE0OS45IEMgMTAwLjIsMTUxLjUgMTAxLjEsMTU1LjkgMTAzLjMsMTYwLjcgQyAxMDUuNSwxNjUuNiAxMDguOSwxNzAuOSAxMTYuNCwxNzYuMiBDIDEyMy45LDE4MS41IDEzNS42LDE4Ni44IDE0Mi45LDE4Ny4xIEMgMTUwLjIsMTg3LjMgMTUzLjEsMTgyLjUgMTU1LjUsMTc3LjYgQyAxNTcuOSwxNzIuOCAxNTkuOSwxNjguMCAxNjEuMSwxNjUuMyBDIDE2Mi4zLDE2Mi43IDE2Mi44LDE2Mi4yIDE2NS43LDE2NS44IEMgMTY4LjYsMTY5LjQgMTczLjksMTc3LjIgMTgwLjcsMTg1LjQgQyAxODcuNSwxOTMuNiAxOTUuOCwyMDIuMyAyMDIuMSwyMDcuNiBDIDIwOC40LDIxMi45IDIxMi44LDIxNC45IDIxOC4zLDIxNS42IEMgMjIzLjksMjE2LjMgMjMwLjcsMjE1LjggMjM1LjYsMjEyLjkgQyAyNDAuNCwyMTAuMCAyNDMuMywyMDQuNyAyNDYuMywxOTcuMiBDIDI0OS4yLDE4OS43IDI1Mi4xLDE4MC4xIDI1My41LDE3NS4wIEMgMjU1LjAsMTY5LjkgMjU1LjAsMTY5LjQgMjU1LjIsMTY5LjIgQyAyNTUuNSwxNjguOSAyNTYuMCwxNjguOSAyNTYuOSwxNjkuNCBDIDI1Ny45LDE2OS45IDI1OS40LDE3MC45IDI2MS41LDE3MS44IEMgMjYzLjcsMTcyLjggMjY2LjYsMTczLjggMjY5LjgsMTc0LjMgQyAyNzIuOSwxNzQuNyAyNzYuMywxNzQuNyAyNzkuMywxNzQuMCBDIDI4Mi4yLDE3My4zIDI4NC42LDE3MS44IDI4Ni41LDE2OS45IEMgMjg4LjUsMTY4LjAgMjg5LjksMTY1LjYgMjkwLjksMTYxLjIgQyAyOTEuOSwxNTYuOSAyOTIuNCwxNTAuNiAyOTAuMiwxNDYuMCBDIDI4OC4wLDE0MS40IDI4My4xLDEzOC41IDI3Ny44LDEzNy4wIEMgMjcyLjUsMTM1LjYgMjY2LjYsMTM1LjYgMjYzLjUsMTM1LjYgQyAyNjAuMywxMzUuNiAyNTkuOCwxMzUuNiAyNTkuNCwxMzYuMSBDIDI1OC45LDEzNi42IDI1OC40LDEzNy41IDI2MS4zLDE0MC45IEMgMjY0LjIsMTQ0LjMgMjcwLjUsMTUwLjEgMjgzLjksMTU2LjEgQyAyOTcuMiwxNjIuMiAzMTcuNiwxNjguNSAzMjkuNSwxNzEuNiBDIDM0MS40LDE3NC43IDM0NC44LDE3NC43IDM1MC44LDE3NC4zIEMgMzU2LjksMTczLjggMzY1LjcsMTcyLjggMzcwLjMsMTcyLjEgQyAzNzQuOSwxNzEuNCAzNzUuNCwxNzAuOSAzNzUuNiwxNzIuMyBDIDM3NS44LDE3My44IDM3NS44LDE3Ny4yIDM3NS44LDE4Mi43IEMgMzc1LjgsMTg4LjMgMzc1LjgsMTk2LjAgMzc1LjYsMjAzLjUgQyAzNzUuNCwyMTEuMCAzNzQuOSwyMTguMiAzNzQuOSwyMjMuMSBDIDM3NC45LDIyNy45IDM3NS40LDIzMC4zIDM3OS43LDIzMS41IEMgMzg0LjEsMjMyLjcgMzkyLjMsMjMyLjcgNDAwLjYsMjMyLjIgQyA0MDguOCwyMzEuOCA0MTcuMSwyMzAuOCA0MjUuNiwyMjkuNiBDIDQzNC4xLDIyOC40IDQ0Mi44LDIyNi45IDQ0OC42LDIyNi4wIEMgNDU0LjUsMjI1LjAgNDU3LjQsMjI0LjUgNDYwLjMsMjI0LjAiIGRhdGEtdGVzdGlkPSJpbmstcGF0aCIgc3R5bGU9InN0cm9rZTogcmdiKDAsIDAsIDApOyBzdHJva2Utd2lkdGg6IDRweDsgcG9pbnRlci1ldmVudHM6IG5vbmU7Ii8+PHBhdGggY2xhc3M9IlBTUERGS2l0LTQ3cjV6eHB0eWtoanFwenFkdWU5YWNwaHR6IFBTUERGS2l0LTQ0OG5jcjVtdWhycGM4cDh5dmFkeTUxZW1jIFBTUERGS2l0LVNtb290aC1MaW5lcyIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZD0iTSAyMTIuNzYzODUwODAyMDQ4MiwxNjYuMDQwNTk0NDE4MjY3NDYgQyAxODMuNiwxNjAuNyAxNTQuNSwxNTUuNCAxMzUuNiwxNTIuMyBDIDExNi43LDE0OS4xIDEwNy45LDE0OC4yIDEwNC4wLDE0OS45IEMgMTAwLjIsMTUxLjUgMTAxLjEsMTU1LjkgMTAzLjMsMTYwLjcgQyAxMDUuNSwxNjUuNiAxMDguOSwxNzAuOSAxMTYuNCwxNzYuMiBDIDEyMy45LDE4MS41IDEzNS42LDE4Ni44IDE0Mi45LDE4Ny4xIEMgMTUwLjIsMTg3LjMgMTUzLjEsMTgyLjUgMTU1LjUsMTc3LjYgQyAxNTcuOSwxNzIuOCAxNTkuOSwxNjguMCAxNjEuMSwxNjUuMyBDIDE2Mi4zLDE2Mi43IDE2Mi44LDE2Mi4yIDE2NS43LDE2NS44IEMgMTY4LjYsMTY5LjQgMTczLjksMTc3LjIgMTgwLjcsMTg1LjQgQyAxODcuNSwxOTMuNiAxOTUuOCwyMDIuMyAyMDIuMSwyMDcuNiBDIDIwOC40LDIxMi45IDIxMi44LDIxNC45IDIxOC4zLDIxNS42IEMgMjIzLjksMjE2LjMgMjMwLjcsMjE1LjggMjM1LjYsMjEyLjkgQyAyNDAuNCwyMTAuMCAyNDMuMywyMDQuNyAyNDYuMywxOTcuMiBDIDI0OS4yLDE4OS43IDI1Mi4xLDE4MC4xIDI1My41LDE3NS4wIEMgMjU1LjAsMTY5LjkgMjU1LjAsMTY5LjQgMjU1LjIsMTY5LjIgQyAyNTUuNSwxNjguOSAyNTYuMCwxNjguOSAyNTYuOSwxNjkuNCBDIDI1Ny45LDE2OS45IDI1OS40LDE3MC45IDI2MS41LDE3MS44IEMgMjYzLjcsMTcyLjggMjY2LjYsMTczLjggMjY5LjgsMTc0LjMgQyAyNzIuOSwxNzQuNyAyNzYuMywxNzQuNyAyNzkuMywxNzQuMCBDIDI4Mi4yLDE3My4zIDI4NC42LDE3MS44IDI4Ni41LDE2OS45IEMgMjg4LjUsMTY4LjAgMjg5LjksMTY1LjYgMjkwLjksMTYxLjIgQyAyOTEuOSwxNTYuOSAyOTIuNCwxNTAuNiAyOTAuMiwxNDYuMCBDIDI4OC4wLDE0MS40IDI4My4xLDEzOC41IDI3Ny44LDEzNy4wIEMgMjcyLjUsMTM1LjYgMjY2LjYsMTM1LjYgMjYzLjUsMTM1LjYgQyAyNjAuMywxMzUuNiAyNTkuOCwxMzUuNiAyNTkuNCwxMzYuMSBDIDI1OC45LDEzNi42IDI1OC40LDEzNy41IDI2MS4zLDE0MC45IEMgMjY0LjIsMTQ0LjMgMjcwLjUsMTUwLjEgMjgzLjksMTU2LjEgQyAyOTcuMiwxNjIuMiAzMTcuNiwxNjguNSAzMjkuNSwxNzEuNiBDIDM0MS40LDE3NC43IDM0NC44LDE3NC43IDM1MC44LDE3NC4zIEMgMzU2LjksMTczLjggMzY1LjcsMTcyLjggMzcwLjMsMTcyLjEgQyAzNzQuOSwxNzEuNCAzNzUuNCwxNzAuOSAzNzUuNiwxNzIuMyBDIDM3NS44LDE3My44IDM3NS44LDE3Ny4yIDM3NS44LDE4Mi43IEMgMzc1LjgsMTg4LjMgMzc1LjgsMTk2LjAgMzc1LjYsMjAzLjUgQyAzNzUuNCwyMTEuMCAzNzQuOSwyMTguMiAzNzQuOSwyMjMuMSBDIDM3NC45LDIyNy45IDM3NS40LDIzMC4zIDM3OS43LDIzMS41IEMgMzg0LjEsMjMyLjcgMzkyLjMsMjMyLjcgNDAwLjYsMjMyLjIgQyA0MDguOCwyMzEuOCA0MTcuMSwyMzAuOCA0MjUuNiwyMjkuNiBDIDQzNC4xLDIyOC40IDQ0Mi44LDIyNi45IDQ0OC42LDIyNi4wIEMgNDU0LjUsMjI1LjAgNDU3LjQsMjI0LjUgNDYwLjMsMjI0LjAiIGRhdGEtdGVzdGlkPSJjbGlja2FibGUtcGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogNXB4OyIvPjwvZz48L2c+PC9zdmc+';
const svg = window.atob(base64Image);
svgToPng(svg,(imgData)=>{
const pngImage = document.createElement('img');
document.body.appendChild(pngImage);
pngImage.src=imgData;
});
function svgToPng(svg, callback) {
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) => {
callback(imgData);
URL.revokeObjectURL(url);
});
}
function getSvgUrl(svg) {
return  URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
}
function svgUrlToPng(svgUrl, callback) {
const svgImage = document.createElement('img');
// imgPreview.style.position = 'absolute';
// imgPreview.style.top = '-9999px';
document.body.appendChild(svgImage);
svgImage.onload = function () {
const canvas = document.createElement('canvas');
canvas.width = svgImage.clientWidth;
canvas.height = svgImage.clientHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
const imgData = canvas.toDataURL('image/png');
console.log(imgData, 'imgData') // This logged here gives different image in chrome and safari
callback(imgData);
// document.body.removeChild(imgPreview);
};
svgImage.src = svgUrl;
}

drawImage()使用没有固有宽度或高度的SVG图像是互操作的噩梦。
目前规格文本确实遵循了Chrome的行为,但鉴于他们是唯一一个实现它的,很难称之为"标准"。

这里的一个问题是,你是根据<img>元素的clientWidthclientHeight属性设置画布大小的。
这个<img>元素嵌入了一个没有自己固有宽度和高度的SVG图像。因此,默认的width被解释为100%.
所以你的<img>元素将占用容器(<body>)的大小,并且你将设置画布的大小为该主体的大小。

然而,这个大小应该只影响<img>元素,当它的图像绘制在画布上时,这个<img>元素应该(大部分)无关。

在这个没有固有宽度或高度的图像的特殊情况下,重要的是输出画布的大小。在这种情况下,当前规范文本要求使用CSS默认大小算法来确定图像的大小,这基本上是说,如果定义了图像的固有宽度和高度,或者如果图像具有定义的固有宽高比(例如viewBox),则将可用值乘以该比率,如果两者都不存在,则使用默认大小。
这个默认大小是由输出的画布大小决定的。

这里,<svg>既没有width也没有height,但它确实有一个固有的比例(110/374.0449438202247),所以width将被设置为画布的宽度,高度将被设置为这个宽度x比例。

因此,drawImage使用的图像的正确计算大小应该取决于画布大小,因此取决于您的用户的屏幕大小,我个人认为这非常令人不安。这就是Chrome所做的。另一方面,Safari使用viewBox中的值来表示widthheight。这是有意义的,但在各种实践中是有问题的(例如,使用0 0 1 1的viewBox来更容易地在SVG中进行绝对定位是很常见的)。

在这种情况下,Firefox根本不会绘制您的图像。

你可以避免这种差异不附加你的<img>,通过设置你的画布宽度和高度基于<img>.naturalWidth.naturalHeight,并通过设置你的根<svg>元素的widthheight的绝对值(即不是%)。
这将给你最跨浏览器的体验,和最可预测的一个(因为Chrome的行为,所有的用户将有不同大小的图像)。

const svg = `<svg
width="374" height="110" ${/* We force width absolute width and height */""}
xmlns="http://www.w3.org/2000/svg" viewBox="92.97752808988764 129 374.0449438202247 110" class="PSPDFKit-8s1sbc513t34q38895b4bh3jvn" focusable="false" data-testid="ink-svg" style="left: 0px; top: 0px; width: 100%; height: 100%; opacity: 1; overflow: hidden;">
<rect x="97.97752808988764" y="134" width="364.0449438202247" height="100" class="PSPDFKit-7871rfhyxb7pdmks1n3x7avvta" style="fill: transparent;"/>
<g style="fill: transparent;">
<g>
<path class="PSPDFKit-47r5zxptykhjqpzqdue9acphtz PSPDFKit-448ncr5muhrpc8p8yvady51emc PSPDFKit-Smooth-Lines"
d="M 212.7638508020482,166.04059441826746 C 183.6,160.7 154.5,155.4 135.6,152.3 C 116.7,149.1 107.9,148.2 104.0,149.9 C 100.2,151.5 101.1,155.9 103.3,160.7 C 105.5,165.6 108.9,170.9 116.4,176.2 C 123.9,181.5 135.6,186.8 142.9,187.1 C 150.2,187.3 153.1,182.5 155.5,177.6 C 157.9,172.8 159.9,168.0 161.1,165.3 C 162.3,162.7 162.8,162.2 165.7,165.8 C 168.6,169.4 173.9,177.2 180.7,185.4 C 187.5,193.6 195.8,202.3 202.1,207.6 C 208.4,212.9 212.8,214.9 218.3,215.6 C 223.9,216.3 230.7,215.8 235.6,212.9 C 240.4,210.0 243.3,204.7 246.3,197.2 C 249.2,189.7 252.1,180.1 253.5,175.0 C 255.0,169.9 255.0,169.4 255.2,169.2 C 255.5,168.9 256.0,168.9 256.9,169.4 C 257.9,169.9 259.4,170.9 261.5,171.8 C 263.7,172.8 266.6,173.8 269.8,174.3 C 272.9,174.7 276.3,174.7 279.3,174.0 C 282.2,173.3 284.6,171.8 286.5,169.9 C 288.5,168.0 289.9,165.6 290.9,161.2 C 291.9,156.9 292.4,150.6 290.2,146.0 C 288.0,141.4 283.1,138.5 277.8,137.0 C 272.5,135.6 266.6,135.6 263.5,135.6 C 260.3,135.6 259.8,135.6 259.4,136.1 C 258.9,136.6 258.4,137.5 261.3,140.9 C 264.2,144.3 270.5,150.1 283.9,156.1 C 297.2,162.2 317.6,168.5 329.5,171.6 C 341.4,174.7 344.8,174.7 350.8,174.3 C 356.9,173.8 365.7,172.8 370.3,172.1 C 374.9,171.4 375.4,170.9 375.6,172.3 C 375.8,173.8 375.8,177.2 375.8,182.7 C 375.8,188.3 375.8,196.0 375.6,203.5 C 375.4,211.0 374.9,218.2 374.9,223.1 C 374.9,227.9 375.4,230.3 379.7,231.5 C 384.1,232.7 392.3,232.7 400.6,232.2 C 408.8,231.8 417.1,230.8 425.6,229.6 C 434.1,228.4 442.8,226.9 448.6,226.0 C 454.5,225.0 457.4,224.5 460.3,224.0"
data-testid="ink-path" style="stroke: rgb(0, 0, 0); stroke-width: 4px; pointer-events: none;"/>
<path class="PSPDFKit-47r5zxptykhjqpzqdue9acphtz PSPDFKit-448ncr5muhrpc8p8yvady51emc PSPDFKit-Smooth-Lines" stroke="transparent"
d="M 212.7638508020482,166.04059441826746 C 183.6,160.7 154.5,155.4 135.6,152.3 C 116.7,149.1 107.9,148.2 104.0,149.9 C 100.2,151.5 101.1,155.9 103.3,160.7 C 105.5,165.6 108.9,170.9 116.4,176.2 C 123.9,181.5 135.6,186.8 142.9,187.1 C 150.2,187.3 153.1,182.5 155.5,177.6 C 157.9,172.8 159.9,168.0 161.1,165.3 C 162.3,162.7 162.8,162.2 165.7,165.8 C 168.6,169.4 173.9,177.2 180.7,185.4 C 187.5,193.6 195.8,202.3 202.1,207.6 C 208.4,212.9 212.8,214.9 218.3,215.6 C 223.9,216.3 230.7,215.8 235.6,212.9 C 240.4,210.0 243.3,204.7 246.3,197.2 C 249.2,189.7 252.1,180.1 253.5,175.0 C 255.0,169.9 255.0,169.4 255.2,169.2 C 255.5,168.9 256.0,168.9 256.9,169.4 C 257.9,169.9 259.4,170.9 261.5,171.8 C 263.7,172.8 266.6,173.8 269.8,174.3 C 272.9,174.7 276.3,174.7 279.3,174.0 C 282.2,173.3 284.6,171.8 286.5,169.9 C 288.5,168.0 289.9,165.6 290.9,161.2 C 291.9,156.9 292.4,150.6 290.2,146.0 C 288.0,141.4 283.1,138.5 277.8,137.0 C 272.5,135.6 266.6,135.6 263.5,135.6 C 260.3,135.6 259.8,135.6 259.4,136.1 C 258.9,136.6 258.4,137.5 261.3,140.9 C 264.2,144.3 270.5,150.1 283.9,156.1 C 297.2,162.2 317.6,168.5 329.5,171.6 C 341.4,174.7 344.8,174.7 350.8,174.3 C 356.9,173.8 365.7,172.8 370.3,172.1 C 374.9,171.4 375.4,170.9 375.6,172.3 C 375.8,173.8 375.8,177.2 375.8,182.7 C 375.8,188.3 375.8,196.0 375.6,203.5 C 375.4,211.0 374.9,218.2 374.9,223.1 C 374.9,227.9 375.4,230.3 379.7,231.5 C 384.1,232.7 392.3,232.7 400.6,232.2 C 408.8,231.8 417.1,230.8 425.6,229.6 C 434.1,228.4 442.8,226.9 448.6,226.0 C 454.5,225.0 457.4,224.5 460.3,224.0"
data-testid="clickable-path" style="stroke-width: 5px;"/>
</g>
</g>
</svg>`;
svgToPng(svg, (imgData) => {
const pngImage = document.createElement('img');
pngImage.src = imgData;
});
function svgToPng(svg, callback) {
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) => {
callback(imgData);
URL.revokeObjectURL(url);
});
}
function getSvgUrl(svg) {
return URL.createObjectURL(new Blob([svg], {
type: 'image/svg+xml'
}));
}
function svgUrlToPng(svgUrl, callback) {
const svgImage = document.createElement('img');
// do not append the <img>
svgImage.onload = function() {
const canvas = document.createElement('canvas');
canvas.width = svgImage.naturalWidth;
canvas.height = svgImage.naturalHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
// show the <canvas> to debug
document.body.append(canvas);
const imgData = canvas.toDataURL('image/png');
callback(imgData);
};
svgImage.src = svgUrl;
}
canvas {
background: chocolate;
}

最新更新