从React组件生成PDF文件

时间:2017-07-08 17:48:09

标签: reactjs pdf meteor

我一直在构建一个轮询应用程序。人们可以创建他们的民意调查并获得有关他们提出的问题的数据。我想添加功能,让用户以PDF格式下载结果。

例如,我有两个组件负责抓取问题和数据。

<QuestionBox />
<ViewCharts />

我正在尝试将这两个组件输出到PDF文件中。然后,用户可以下载此PFD文件。我找到了一些允许在组件内部呈现PDF的包。但是我找不到可以从包含虚拟DOM的输入流生成PDF的方法。如果我想从头开始实现这一目标,我应该遵循哪种方法?

10 个答案:

答案 0 :(得分:63)

渲染反应因为pdf通常很痛苦,但是有一种方法可以使用canvas。

想法是转换: HTML - &gt;画布 - &gt; PNG(或JPEG) - &gt; PDF

要实现上述目标,您需要:

  1. html2canvas&amp;
  2. jsPDF
  3. import React, {Component, PropTypes} from 'react';
    
    // download html2canvas and jsPDF and save the files in app/ext, or somewhere else
    // the built versions are directly consumable
    // import {html2canvas, jsPDF} from 'app/ext';
    
    
    export default class Export extends Component {
      constructor(props) {
        super(props);
      }
    
      printDocument() {
        const input = document.getElementById('divToPrint');
        html2canvas(input)
          .then((canvas) => {
            const imgData = canvas.toDataURL('image/png');
            const pdf = new jsPDF();
            pdf.addImage(imgData, 'JPEG', 0, 0);
            // pdf.output('dataurlnewwindow');
            pdf.save("download.pdf");
          })
        ;
      }
    
      render() {
        return (<div>
          <div className="mb5">
            <button onClick={this.printDocument}>Print</button>
          </div>
          <div id="divToPrint" className="mt4" {...css({
            backgroundColor: '#f5f5f5',
            width: '210mm',
            minHeight: '297mm',
            marginLeft: 'auto',
            marginRight: 'auto'
          })}>
            <div>Note: Here the dimensions of div are same as A4</div> 
            <div>You Can add any component here</div>
          </div>
        </div>);
      }
    }

    此处的代码段不起作用,因为未导入所需的文件。

    this answer中使用了另一种方法,中间步骤被删除,您只需将HTML转换为PDF即可。有一个选项可以在jsPDF文档中执行此操作,但从个人观察,我觉得当dom首先转换为png时可以获得更好的准确性。

    更新0:2018年9月14日

    此方法创建的pdf上的文本将无法选择。如果这是一项要求,您可能会发现this article有帮助。

答案 1 :(得分:2)

您可以使用jsPDF用户canvans

import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';

 _exportPdf = () => {

     html2canvas(document.querySelector("#capture")).then(canvas => {
        document.body.appendChild(canvas);  // if you want see your screenshot in body.
        const imgData = canvas.toDataURL('image/png');
        const pdf = new jsPDF();
        pdf.addImage(imgData, 'PNG', 0, 0);
        pdf.save("download.pdf"); 
    });

 }

您具有ID捕获功能的div是:

示例

<div id="capture">
  <p>Hello in my life</p>
  <span>How can hellp you</span>
</div>

答案 2 :(得分:2)

React-PDF是一个很好的资源。

将您的标记和CSS转换为React-PDF的格式会花费一些时间,但是很容易理解。从中导出PDF非常简单。

要允许用户下载react-PDF生成的PDF,请使用他们的on the fly rendering,它提供了可自定义的下载链接。单击后,该网站将为用户呈现并下载PDF。

这里是他们的REPL,它将使您熟悉所需的标记和样式。他们也有PDF的下载链接,但是这里没有显示代码。

答案 3 :(得分:1)

这可能是次优方法,也可能不是次优,但是我发现的解决多页问题的最简单方法是确保在调用jsPDFObj.save方法之前完成所有渲染。

对于渲染隐藏的文章,这可以通过对CSS图片文本替换进行类似的修复来解决,我将要渲染的元素绝对定位在离开页面左侧-9999px处, 这不会影响布局,并允许elem对html2pdf可见,尤其是在使用基于{display: none}的标签,手风琴和其他UI组件时。

此方法将先决条件包装在promise中,并在pdf.save()方法中调用finally()。我不确定这是万无一失或是反模式的,但似乎在大多数情况下我都可以使用。

// Get List of paged elements.
let elems = document.querySelectorAll('.elemClass');
let pdf = new jsPDF("portrait", "mm", "a4");

// Fix Graphics Output by scaling PDF and html2canvas output to 2
pdf.scaleFactor = 2;

// Create a new promise with the loop body
let addPages = new Promise((resolve,reject)=>{
  elems.forEach((elem, idx) => {
    // Scaling fix set scale to 2
    html2canvas(elem, {scale: "2"})
      .then(canvas =>{
        if(idx < elems.length - 1){
          pdf.addImage(canvas.toDataURL("image/png"), 0, 0, 210, 297);
          pdf.addPage();
        } else {
          pdf.addImage(canvas.toDataURL("image/png"), 0, 0, 210, 297);
          console.log("Reached last page, completing");
        }
  })
  
  setTimeout(resolve, 100, "Timeout adding page #" + idx);
})

addPages.finally(()=>{
   console.log("Saving PDF");
   pdf.save();
});

答案 4 :(得分:1)

npm install jspdf --save

//反应代码

import jsPDF from 'jspdf';


var doc = new jsPDF()


 doc.fromHTML("<div>JOmin</div>", 1, 1)


onclick //

 doc.save("name.pdf")

答案 5 :(得分:1)

您可以使用ReactPDF

让您轻松地将div转换为PDF。您需要匹配现有的标记才能使用ReactPDF标记,但这是值得的。

答案 6 :(得分:1)

my opinion : 

import { useRef } from "react";
import { jsPDF } from "jspdf";
import html2canvas from "html2canvas";

import "./styles.css";

const App = () => {
  const inputRef = useRef(null);
  const printDocument = () => {
    html2canvas(inputRef.current).then((canvas) => {
      const imgData = canvas.toDataURL("image/png");
      const pdf = new jsPDF();
      pdf.addImage(imgData, "JPEG", 0, 0);
      pdf.save("download.pdf");
    });
  };
  return (
    <>
      <div className="App">
        <div className="mb5">
          <button onClick={printDocument}>Print</button>
        </div>
        <div id="divToPrint" ref={inputRef}>
          <div>Note: Here the dimensions of div are same as A4</div>
          <div>You Can add any component here</div>
        </div>
      </div>
    </>
  );
};
export default App;

link demo:

https://codesandbox.io/s/frosty-sea-44b4q?file=/src/App.js:0-907

答案 7 :(得分:0)

您可以使用ReactDOMServer将组件呈现为HTML,然后在jsPDF上使用它。

首先进行导入:

import React from "react";
import ReactDOMServer from "react-dom/server";

然后:

var doc = new jsPDF();
doc.fromHTML(ReactDOMServer.renderToStaticMarkup(this.render()));
doc.save("myDocument.pdf");

首选:

  

renderToStaticMarkup

代替:

  

renderToString

由于前者包含了可依赖的HTML代码。

答案 8 :(得分:0)

我使用了jsPDF和html-to-image。

您可以在下面的git repo上查看代码。

Link

如果愿意,可以在这里放一颗星星✌️

答案 9 :(得分:0)

只有几个步骤。我们可以从HTML页面下载或生成PDF,也可以从HTML页面生成特定div的PDF。

步骤:HTML->图片(PNG或JPEG)-> PDF

请按照以下步骤操作,

第1步:-

npm install --save html-to-image
npm install jspdf --save

第2步:-

/* ES6 */
import * as htmlToImage from 'html-to-image';
import { toPng, toJpeg, toBlob, toPixelData, toSvg } from 'html-to-image';
 
/* ES5 */
var htmlToImage = require('html-to-image');

-------------------------
import { jsPDF } from "jspdf";

第3步:-

   ******  With out PDF properties given below  ******

 htmlToImage.toPng(document.getElementById('myPage'), { quality: 0.95 })
        .then(function (dataUrl) {
          var link = document.createElement('a');
          link.download = 'my-image-name.jpeg';
          const pdf = new jsPDF();          
          pdf.addImage(dataUrl, 'PNG', 0, 0);
          pdf.save("download.pdf"); 
        });


    ******  With PDF properties given below ******

    htmlToImage.toPng(document.getElementById('myPage'), { quality: 0.95 })
        .then(function (dataUrl) {
          var link = document.createElement('a');
          link.download = 'my-image-name.jpeg';
          const pdf = new jsPDF();
          const imgProps= pdf.getImageProperties(dataUrl);
          const pdfWidth = pdf.internal.pageSize.getWidth();
          const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
          pdf.addImage(dataUrl, 'PNG', 0, 0,pdfWidth, pdfHeight);
          pdf.save("download.pdf"); 
        });

我认为这很有帮助。请尝试