【问题标题】:Download SVG element with custom font to PNG将具有自定义字体的 SVG 元素下载到 PNG
【发布时间】:2021-11-08 11:30:49
【问题描述】:

我正在尝试向我正在构建的应用程序添加一项功能,该功能允许用户将渲染的 SVG 元素下载为 PNG 文件。该应用程序是使用 React 编写的,我决定使用 save-svg-to-png 包来实现这一点。到目前为止一切正常,但我有一个使用粗体 Roboto 字体的元素的问题 - 下载图像后,字体回落到默认值(Times New Roman 或类似的东西)。现在,我知道在可以传递给saveSvgToPng 函数的options 对象中,有一个fonts 参数,其签名如下:

fonts - {text, url, format} 对象的列表,指定要在 SVG 中内联的字体。省略此选项默认为自动检测字体规则。

但是没有使用示例来说明这一点,我也无法在网上找到任何示例。我遇到的问题似乎与url 属性有关-我不确定要在其中包含什么以使字体正确呈现。我尝试使用字体的 base64 编码生成一个 URI,并从 Google 字体网站 (https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap) 添加 URL,但似乎没有一个工作。谁能帮我解决这个问题?

【问题讨论】:

标签: javascript reactjs svg fonts


【解决方案1】:

您需要在 url 参数中提供指向实际字体文件 (.woff) 的 URI,并在 text 参数中提供 @font-face 规则。

注意your url指向的文件只是一个CSS文件,实际的字体文件还是要从那里掌握(如果你需要编程的方式,我之前写过something你可能可以重复使用) .

所以在你的情况下你必须通过(假设你只有拉丁范围内的字形)

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>

【讨论】:

    【解决方案2】:

    我并不那么乐观。 Using Fonts in SVGcss - How do I use a custom font in an SVG image on my site? - Graphic Design Stack ExchangeSVG text and Small, Scalable, Accessible Typographic Designs | CSS-Tricks 或多或少得出结论,当使用 &lt;img&gt; 显示自定义字体时,无法在 SVG 中显示自定义字体。我知道这没有回答您的问题,但以下示例显示了如何将内联 SVG 加载到图像对象中,在 &lt;canvas&gt; 中绘制并作为数据 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 Longsons 的初始评论):字体应作为数据 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>

    【讨论】:

    • @RobertLongson 你是对的。我一开始没有测试数据 URL,但显然这是唯一可行的方法。我更新了我的答案。
    • 问题是关于this library 以及如何使用它的font 选项。
    • @Kaiido 我知道,但是如果你滚动浏览saveSvgAsPng/saveSvgAsPng.js,你可以看到他们如何使用画布等来生成 PNG,就像我在我的示例中所做的那样,以证明它是可能的并且它可以使用数据 URL 来完成。
    猜你喜欢
    • 2011-11-11
    • 2016-10-12
    • 1970-01-01
    • 2013-03-07
    • 2017-10-13
    • 2017-05-30
    • 2022-08-20
    • 2013-01-14
    • 2012-11-23
    相关资源
    最近更新 更多