将带有自定义字体的 SVG 元素下载到 PNG
Download SVG element with custom font to PNG
我正在尝试向我正在构建的应用程序添加一项功能,让用户可以将呈现的 SVG 元素下载为 PNG 文件。该应用程序是使用 React 编写的,我决定使用 save-svg-to-png
包来实现这一点。到目前为止一切正常,但我对使用粗体 Roboto 字体的元素有问题 - 下载图像后,字体会恢复为默认字体(Times New Roman 或类似字体)。现在,我知道在可以传递给 saveSvgToPng
函数的 options
对象中有一个 fonts
参数,其签名如下:
fonts - A list of {text, url, format} objects the specify what fonts to inline in the SVG. Omitting this option defaults to auto-detecting font rules.
但是没有使用示例来阐明这一点,我也无法在网上找到任何示例。我遇到的问题似乎与 url
属性 有关 - 我不确定要在其中包含什么以使字体正确呈现。我尝试使用字体的 base64 编码生成 URI,并从 Google 字体站点 (https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap
) 添加 URL,但 none 似乎有效。谁能帮我解决这个问题?
我不是很乐观。
Using Fonts in SVG 和
css - How do I use a custom font in an SVG image on my site? - Graphic Design Stack Exchange 和
SVG text
and Small, Scalable, Accessible Typographic Designs | CSS-Tricks 或多或少得出结论,当使用 <img>
显示时,无法在 SVG 中显示自定义字体。我知道这没有回答您的问题,但以下示例显示了如何将内联 SVG 加载到图像对象中,在 <canvas>
中绘制并作为数据 URL 导出到 PNG 图像。这是将 SVG 转换为 PNG 的基本过程,您可以看到它破坏了自定义字体。
document.addEventListener('DOMContentLoaded', e => {
let img = document.images[0];
let svg = document.getElementById('svg');
let image = new Image();
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
image.addEventListener('load', e => {
ctx.drawImage(e.target, 0, 0, 300, 30);
img.src = canvas.toDataURL("image/png");
});
image.src = "data:image/svg+xml," + svg.outerHTML;
});
<p>SVG image:</p>
<svg id="svg" viewBox="0 0 100 10" width="300" height="30" xmlns="http://www.w3.org/2000/svg">
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap');
svg{
font-family: Roboto, sans-serif;
}
text {
font-family:'Roboto';
font-weight: bold;
}
</style>
<text font-size="10" text-anchor="middle" dominant-baseline="middle" x="50" y="5">Test</text>
</svg>
<p>Canvas image:</p>
<canvas id="canvas" width="300" height="30"></canvas>
<p>PNG image:</p>
<p><img /></p>
更新
好的,现在更乐观了...我用字体作为数据 URL 测试了我的示例,它确实有效。在下面的示例中,我将字体作为数据嵌入 URL(不幸的是字体数据太多,所以你只是一个占位符)。
这是结论(也是Robert Longson的初始评论):字体应该作为数据嵌入URL。
document.addEventListener('DOMContentLoaded', e => {
let img = document.images[0];
let svg = document.getElementById('svg');
let image = new Image();
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
image.addEventListener('load', e => {
ctx.drawImage(e.target, 0, 0, 300, 30);
img.src = canvas.toDataURL("image/png");
});
image.src = "data:image/svg+xml," + svg.outerHTML;
});
<p>SVG image:</p>
<svg id="svg" viewBox="0 0 100 10" width="300" height="30" xmlns="http://www.w3.org/2000/svg">
<style>
<![CDATA[
@font-face {
font-family: Roboto;
src: url('data:font/ttf;base64,[ ---- font data goes here ------ ]') format("truetype");
}
svg{
font-family: Roboto, sans-serif;
}
text {
font-family: Roboto;
}
]]>
</style>
<text font-size="10" text-anchor="middle" dominant-baseline="middle" x="50" y="5">Test</text>
</svg>
<p>Canvas image:</p>
<canvas id="canvas" width="300" height="30"></canvas>
<p>PNG image:</p>
<p><img /></p>
您需要在 url
参数中提供指向实际字体文件 (.woff) 的 URI,并在 text
参数中提供 @font-face
规则。
请注意,your url points to is just a CSS file, the actual font files are still to be grasped from there (if you need a programmatic way, I wrote 之前的文件您可能会重复使用)。
所以在你的情况下你必须通过(假设你只有拉丁语范围内的字形)
saveSvgAsPng(element, file_name, {
fonts: [
{
url: "https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2",
text: `
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}`
}
]
});
// using svgAsDataUri instead of saveSvgAsPng to avoid downloading the file
svgAsDataUri(document.getElementById("target"), {
fonts: [
{
url: "https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2",
text: `
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}`
}
]
}).then( uri => {
const img = new Image();
img.src = uri;
document.body.append(img);
});
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap");
<script src="https://cdn.jsdelivr.net/gh/exupero/saveSvgAsPng/src/saveSvgAsPng.js"></script>
<svg id="target">
<text font-family="Roboto" y="50">My text</text>
</svg>
我正在尝试向我正在构建的应用程序添加一项功能,让用户可以将呈现的 SVG 元素下载为 PNG 文件。该应用程序是使用 React 编写的,我决定使用 save-svg-to-png
包来实现这一点。到目前为止一切正常,但我对使用粗体 Roboto 字体的元素有问题 - 下载图像后,字体会恢复为默认字体(Times New Roman 或类似字体)。现在,我知道在可以传递给 saveSvgToPng
函数的 options
对象中有一个 fonts
参数,其签名如下:
fonts - A list of {text, url, format} objects the specify what fonts to inline in the SVG. Omitting this option defaults to auto-detecting font rules.
但是没有使用示例来阐明这一点,我也无法在网上找到任何示例。我遇到的问题似乎与 url
属性 有关 - 我不确定要在其中包含什么以使字体正确呈现。我尝试使用字体的 base64 编码生成 URI,并从 Google 字体站点 (https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap
) 添加 URL,但 none 似乎有效。谁能帮我解决这个问题?
我不是很乐观。
Using Fonts in SVG 和
css - How do I use a custom font in an SVG image on my site? - Graphic Design Stack Exchange 和
SVG text
and Small, Scalable, Accessible Typographic Designs | CSS-Tricks 或多或少得出结论,当使用 <img>
显示时,无法在 SVG 中显示自定义字体。我知道这没有回答您的问题,但以下示例显示了如何将内联 SVG 加载到图像对象中,在 <canvas>
中绘制并作为数据 URL 导出到 PNG 图像。这是将 SVG 转换为 PNG 的基本过程,您可以看到它破坏了自定义字体。
document.addEventListener('DOMContentLoaded', e => {
let img = document.images[0];
let svg = document.getElementById('svg');
let image = new Image();
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
image.addEventListener('load', e => {
ctx.drawImage(e.target, 0, 0, 300, 30);
img.src = canvas.toDataURL("image/png");
});
image.src = "data:image/svg+xml," + svg.outerHTML;
});
<p>SVG image:</p>
<svg id="svg" viewBox="0 0 100 10" width="300" height="30" xmlns="http://www.w3.org/2000/svg">
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap');
svg{
font-family: Roboto, sans-serif;
}
text {
font-family:'Roboto';
font-weight: bold;
}
</style>
<text font-size="10" text-anchor="middle" dominant-baseline="middle" x="50" y="5">Test</text>
</svg>
<p>Canvas image:</p>
<canvas id="canvas" width="300" height="30"></canvas>
<p>PNG image:</p>
<p><img /></p>
更新
好的,现在更乐观了...我用字体作为数据 URL 测试了我的示例,它确实有效。在下面的示例中,我将字体作为数据嵌入 URL(不幸的是字体数据太多,所以你只是一个占位符)。
这是结论(也是Robert Longson的初始评论):字体应该作为数据嵌入URL。
document.addEventListener('DOMContentLoaded', e => {
let img = document.images[0];
let svg = document.getElementById('svg');
let image = new Image();
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
image.addEventListener('load', e => {
ctx.drawImage(e.target, 0, 0, 300, 30);
img.src = canvas.toDataURL("image/png");
});
image.src = "data:image/svg+xml," + svg.outerHTML;
});
<p>SVG image:</p>
<svg id="svg" viewBox="0 0 100 10" width="300" height="30" xmlns="http://www.w3.org/2000/svg">
<style>
<![CDATA[
@font-face {
font-family: Roboto;
src: url('data:font/ttf;base64,[ ---- font data goes here ------ ]') format("truetype");
}
svg{
font-family: Roboto, sans-serif;
}
text {
font-family: Roboto;
}
]]>
</style>
<text font-size="10" text-anchor="middle" dominant-baseline="middle" x="50" y="5">Test</text>
</svg>
<p>Canvas image:</p>
<canvas id="canvas" width="300" height="30"></canvas>
<p>PNG image:</p>
<p><img /></p>
您需要在 url
参数中提供指向实际字体文件 (.woff) 的 URI,并在 text
参数中提供 @font-face
规则。
请注意,your url points to is just a CSS file, the actual font files are still to be grasped from there (if you need a programmatic way, I wrote
所以在你的情况下你必须通过(假设你只有拉丁语范围内的字形)
saveSvgAsPng(element, file_name, {
fonts: [
{
url: "https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2",
text: `
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}`
}
]
});
// using svgAsDataUri instead of saveSvgAsPng to avoid downloading the file
svgAsDataUri(document.getElementById("target"), {
fonts: [
{
url: "https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2",
text: `
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}`
}
]
}).then( uri => {
const img = new Image();
img.src = uri;
document.body.append(img);
});
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap");
<script src="https://cdn.jsdelivr.net/gh/exupero/saveSvgAsPng/src/saveSvgAsPng.js"></script>
<svg id="target">
<text font-family="Roboto" y="50">My text</text>
</svg>