使用jsPDF无法将SVG保存为PDF

3

我正在尝试使用jsPDF库下载包含SVG内容的PDF文件,它能够下载文件,但其中没有任何内容,是空白的PDF。

这是我的代码:

const downloadPDF = (goJSDiagram) => {
  const svg = goJSDiagram.makeSvg({scale: 1, background: "white"});
  const svgStr = new XMLSerializer().serializeToString(svg);
  const pdfDoc = new jsPDF();
  pdfDoc.addSvgAsImage(svgStr, 0, 0, pdfDoc.internal.pageSize.width, pdfDoc.internal.pageSize.height)
  pdfDoc.save(props.model[0].cName?.split(" (")[0] + ".pdf");
}

当我执行console.log(svgStr)时,我可以看到SVG XML字符串。我应该进行哪些更改才能在PDF中呈现内容?

你确定 pdfDoc.internal.pageSize.width 或者 ...height 返回了一个值并且这个值大于0吗?你可以尝试一些固定的值来测试它。 - Arnau
我尝试了像pdfDoc.addSvgAsImage(svgStr, 0, 200, 500, 500)这样的值,但结果仍然相同。 - this.srivastava
你有使用单步调试器进行故障排除吗?如果没有,为什么不呢? - Dai
这与react无关。 - oligofren
是的,在React项目中做这个,我本以为要添加React代码,但其实不必,谢谢! - this.srivastava
我看到你找到了一种方法来绕过它,使用光栅化矢量图像的黑客,但是如果你再次尝试调试这个问题,看看是什么原因引起的会很有趣。它肯定看起来像你正在向API传递正确的道具,所以你是否检查它们是否都具有预期的值(例如,不全为0)? https://raw.githack.com/MrRio/jsPDF/master/docs/module-svg.html#%7EaddSvgAsImage - oligofren
3个回答

3

这个Github问题得到良好提示后,我想我知道发生了什么:

存在addSvgAsImage()是异步的问题。

在调用save之前,您没有等待调用完成!这意味着您尝试在SVG开始渲染到PDF之前保存。

请查看相关代码,代码非常简单:

  jsPDFAPI.addSvgAsImage = function(
  // ... bla bla
 return loadCanvg()
      .then(
        function(canvg) {
          return canvg.fromString(ctx, svg, options);
        },
        function() {
          return Promise.reject(new Error("Could not load canvg."));
        }
      )
      .then(function(instance) {
        return instance.render(options);
      })
      .then(function() {
        doc.addImage(
          canvas.toDataURL("image/jpeg", 1.0),
          x,
          y,
          w,
          h,
          compression,
          rotation
        );
      });

正如你所看到的,它只是 Thenable 链。因此,你只需要等待 Promise,这意味着你的代码在 ES2015+ 中看起来应该像这样:

const downloadPDF = async (goJSDiagram) => {
  const svg = goJSDiagram.makeSvg({scale: 1, background: "white"});
  const svgStr = new XMLSerializer().serializeToString(svg);
  const pdfDoc = new jsPDF();
  await pdfDoc.addSvgAsImage(svgStr, 0, 0, pdfDoc.internal.pageSize.width, pdfDoc.internal.pageSize.height)
  pdfDoc.save(props.model[0].cName?.split(" (")[0] + ".pdf");
}

1
你说得对,我需要等待 Promise,但即使更改了宽度和高度,图像在 PDF 中仍然无法正确适配。 - this.srivastava

1

经过大量搜索,我找到了正确的方法来实现这个功能,尽管呈现的内容有点模糊。

  const waitForImage = imgElem => new Promise(resolve => imgElem.complete ? resolve() : imgElem.onload = imgElem.onerror = resolve);

  const downloadPDF = async (goJSDiagram) => {
    const svg = goJSDiagram.makeSvg({scale: 1, background: "white"});
    const svgStr = new XMLSerializer().serializeToString(svg);
    const img = document.createElement('img');
    img.src = 'data:image/svg+xml;base64,' + window.btoa(svgStr);

    waitForImage(img)
      .then(_ => {
        const canvas = document.createElement('canvas');
        canvas.width = 500;
        canvas.height = 500;
        canvas.getContext('2d').drawImage(img, 0, 0, 500, 500);
        const pdfDoc = new jsPDF('p', 'pt', 'a4');
        pdfDoc.addImage(canvas.toDataURL('image/png', 1.0), 0, 200, 500, 500);
        pdfDoc.save(props.model[0].cName?.split(" (")[0] + ".pdf");
      });
  }

如果您将矢量图像转换为根本上是矢量格式(PDF)的格式以供使用,则这几乎不是正确的方法,尽管这可能是一种方法;-) 原始方法才是正确的方法,但您需要调试一下才能弄清楚失败的原因。 - oligofren
1
在查看JSPDF代码后,他们也将SVG转换为光栅图,说实话这有点可惜。他们本可以得到清晰的矢量图形。不确定为什么他们选择了这条路。也许是最简单/最便宜的方式? - oligofren

0

doc.addSvgAsImage(imgData, 20, 50, 17, 20 );
doc.save('filename');

save()方法在doc.addImage(...)之前执行。

当我将doc.save()放入小的setTimeout()中时,它对我起作用。 另一种解决方案是将SVG转换为JPEG,并使用addImage()方法而不是addSvgAsImage()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接