web-dev-qa-db-fra.com

Générer un PDF fichier de React Components

Je construis une application de vote. Les personnes peuvent créer leurs sondages et obtenir des données sur les questions qu’elles posent. J'aimerais ajouter cette fonctionnalité pour permettre aux utilisateurs de télécharger les résultats sous la forme d'un fichier PDF.

Par exemple, j'ai deux composants qui sont chargés de saisir la question et les données.

<QuestionBox />
<ViewCharts />

J'essaie d'extraire les deux composants dans un fichier PDF. L'utilisateur peut ensuite télécharger ce fichier PFD. J'ai trouvé quelques packages permettant le rendu d'un PDF dans un composant. Cependant, je n'ai pas réussi à en trouver un qui puisse générer PDF à partir d'un flux d'entrée constitué d'un DOM virtuel. Si je veux réaliser cela à partir de zéro, quelle approche dois-je suivre? 

23
Ozan

Le rendu en tant que pdf est généralement pénible, mais il existe un moyen de le contourner en utilisant canvas. 

L'idée est de convertir: HTML -> Toile -> PNG (ou JPEG) -> PDF

Pour atteindre ce qui précède, vous aurez besoin de:

  1. html2canvas
  2. jsPDF

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>);
  }
}

L'extrait de code ne fonctionnera pas ici car les fichiers requis ne sont pas importés.

Une autre approche est utilisée dans cette réponse , où les étapes intermédiaires sont supprimées et vous pouvez simplement convertir du HTML au PDF. Il existe une option pour le faire dans la documentation jsPDF également, mais d’observation personnelle, j’estime qu’une meilleure précision est obtenue lorsque dom est d'abord converti en png.

Mise à jour 0: 14 septembre 2018

Le texte sur les fichiers PDF créés par cette approche ne sera pas sélectionnable. Si cela est nécessaire, vous pourriez trouver cet article utile.

57
Shivek Khurana
npm install jspdf --save

// code sur react 

import jsPDF from 'jspdf';


var doc = new jsPDF()


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


onclick //

 doc.save("name.pdf")
1

Ce n'est peut-être pas une façon optimale de faire les choses, mais la solution la plus simple au problème de plusieurs pages que j'ai trouvé était de s'assurer que tout le rendu était fait avant d'appeler la méthode jsPDFObj.save.

En ce qui concerne le rendu des articles cachés, cela est résolu avec un correctif similaire au remplacement du texte de l'image css, je positionne absolument l'élément à restituer -9999px en dehors de la page de gauche, visible pour html2pdf, en particulier lors de l'utilisation d'onglets, accordéons et autres composants d'interface utilisateur dépendant de {display: none}.

Cette méthode encapsule les conditions préalables dans une promesse et appelle pdf.save() dans la méthode finally(). Je ne peux pas être sûr que c'est infaillible, ou un anti-modèle, mais il semblerait que cela fonctionne dans la plupart des cas, je l'ai jeté.

// 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();
});

1
Tech1337

Vous pouvez utiliser ReactDOMServer pour rendre votre composant au format HTML, puis l’utiliser sur jsPDF.

D'abord faire les importations:

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

puis:

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

Préfère utiliser:

renderToStaticMarkup

au lieu de:

renderToString

Comme les premiers incluent le code HTML qui réagit repose.

0
Rodrigo Alencar