将 SVG 转换为 PNG/JPEG,而不在无服务器函数中使用画布
Posted
技术标签:
【中文标题】将 SVG 转换为 PNG/JPEG,而不在无服务器函数中使用画布【英文标题】:Convert a SVG to a PNG/JPEG without using canvas in a serverless function 【发布时间】:2020-12-31 10:21:59 【问题描述】:我有一个无服务器函数,它根据作为查询参数发送的文本返回 SVG。
module.exports = (req, res) =>
const yourName = req.query;
res.setHeader("Content-Type", "image/svg+xml");
const svg = `
<svg fill="none" viewBox="0 0 600 400" xmlns="http://www.w3.org/2000/svg">
<foreignObject >
<div xmlns="http://www.w3.org/1999/xhtml">
<style>
.title
font-size: 10vh;
font-weight: regular;
background-color: #ffddff;
</style>
<p class="title">Hello $yourName</p>
</div>
</foreignObject>
</svg>
`;
res.send(svg);
;
例如,当我调用 /api?yourName=world
时,它会返回以下 SVG;
(部署网址:https://vercel-test-ruddy.vercel.app/api?world)
我需要什么?
我需要将 SVG 作为图像 (PNG/JPEG) 返回。 所以,我的函数看起来像,
module.exports = (req, res) =>
const yourName = req.query;
res.setHeader("Content-Type", "image/png");
const svg = `
<svg fill="none" viewBox="0 0 600 400" xmlns="http://www.w3.org/2000/svg">
<foreignObject >
<div xmlns="http://www.w3.org/1999/xhtml">
<style>
.title
font-size: 10vh;
font-weight: regular;
background-color: #ffddff;
</style>
<p class="title">Hello $yourName</p>
</div>
</foreignObject>
</svg>
`;
res.send( svgToPng(svg) );
;
svgToPng = (svg) =>
// not defined
我是否参考了其他 SO 问题?
是的。关于将 SVG 转换为 PNG 的问题很少:
Convert SVG to image (JPEG, PNG, etc.) in the browser How to Exporting SVG as PNG format
几乎每个答案都谈到将 SVG 转换为画布,然后将画布转换为 PNG。而且答案也更老了。我们可以不使用画布吗?
在我的项目中,我实际上没有 html document
,我只有一个 .js
文件(据我所知,我们需要 HTML5 才能使用画布)。
注意事项
我使用 Vercel 来部署我的无服务器功能;
部署在:https://vercel-test-ruddy.vercel.app/api?yourName=YOUR_NAME_HERE GitRepo:https://github.com/tharindusathis/vercel-test
【问题讨论】:
要将 SVG 转换为位图,需要将其光栅化 (en.wikipedia.org/wiki/Rasterisation) 在通过toBlob
或 toDataURL
编码图像文件时,Canvas 只是一个像素存储。光栅化非常复杂(如果你想要质量),并且在没有 GPU(或同等处理能力)的 JS 中速度会非常慢。将位图编码为 img 文件很容易。您可能可以使用许多 JS 开源编码器。光栅化SVG 请教vercel 文档 请问后端是什么类型的?有 SVG 光栅化组件吗?
补充@Blindman67 所说的,这里你甚至不需要just 一个svg 渲染器,你需要一个HTML + CSS 渲染器(来渲染foreignObject)。我所知道的唯一能够做到这一点的 SVG 渲染器是 Web 浏览器。 “在我的项目中我实际上没有 html 文档”是什么意思?这个项目是从哪里跑的?如果只是从服务器上,那么你也许可以考虑运行一个像 PhantomJS 这样的无头浏览器,虽然我不知道 vercel 是否可以做这样的事情(我真的不知道 Vercel)。
【参考方案1】:
到目前为止,我发现处理这个问题的最佳方法是使用无头 chrome 并获取屏幕截图。例如,使用puppeteer
我可以获得如下视口的屏幕截图,
const browser = await puppeteer.launch();
const page = await browser.newPage();
let yourName = 'world'
const svg = `
<svg fill="none" viewBox="0 0 600 400" xmlns="http://www.w3.org/2000/svg">
<foreignObject >
<div xmlns="http://www.w3.org/1999/xhtml">
<style>
.title
font-size: 10vh;
font-weight: regular;
background-color: #ffddff;
</style>
<p class="title">Hello $yourName</p>
</div>
</foreignObject>
</svg>
`;
await page.setViewport( width: 2048, height: 1170 );
await page.setContent(svg);
console.log(await page.content());
await page.screenshot(path: 'screenshot.png');
await browser.close();
然后很容易发送png
作为响应。
【讨论】:
这是我找了 3 天的那个。这是foreignObject tainted canvas问题的真正解决方案。【参考方案2】:看起来您正在解构查询参数,在这种情况下,它会在查询中搜索该特定对象属性。
如果您明确使用 yourName 作为查询参数,它应该可以工作。
https://vercel-test-ruddy.vercel.app/api?yourName=world
或者不要解耦 req.query 对象属性。
<iframe src="https://vercel-test-ruddy.vercel.app/api?yourName=world" width="450px" height="450px">
【讨论】:
感谢您的建议,但如果不发送yourName
作为查询参数,它完全可以正常工作。在这种情况下,它只会显示为undefined
。 https://vercel-test-ruddy.vercel.app/api。其实这不是什么大问题。
如果这有帮助,请标记为答案。除非您将 yourName 添加为查询参数,否则它不会起作用。否则返回未定义。我的假设是它应该提供一个值。如果不是这种情况,请更新您的问题。谢谢
我认为您误解了这个问题。我需要的是返回png
图像而不是svg
。它目前按预期返回svg
。所以,如果你能再读一遍这个问题,那就更好了。无论如何感谢您的努力。以上是关于将 SVG 转换为 PNG/JPEG,而不在无服务器函数中使用画布的主要内容,如果未能解决你的问题,请参考以下文章
将大型光栅图形图像(位图、PNG、JPEG 等)转换为非矢量 postscript
sh 将PDF文件转换为PNG,JPEG,TEXT和HTML文件
Linq to CSV:将数据转换为 .CSV 文件并从操作中返回文件,而不在服务器上保存文件