113 votes

Générer un fichier PDF à partir de composants React

J'ai créé une application de sondage. Les gens peuvent créer leurs propres sondages et obtenir des données concernant la ou les questions qu'ils posent. J'aimerais ajouter une fonctionnalité permettant aux utilisateurs de télécharger les résultats sous la forme d'un PDF.

Par exemple, j'ai deux composants qui sont responsables de la saisie de la question et des 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 paquets qui permettent le rendu d'un PDF à l'intérieur d'un composant. Cependant, je n'en ai pas trouvé un qui puisse générer un 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 ?

163voto

Shivek Khurana Points 840

Le rendu de react en 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 -> Canvas -> PNG (ou JPEG) -> PDF

Pour réaliser 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>); } }

Le snippet ne fonctionnera pas ici car les fichiers requis ne sont pas importés.

Une autre approche est utilisée dans cette réponse qui permet de supprimer les étapes intermédiaires et de convertir simplement du HTML au PDF. Il y a une option pour faire cela dans la documentation de jsPDF aussi, mais d'après mon observation personnelle, j'ai l'impression qu'une meilleure précision est obtenue lorsque dom est converti en png d'abord.

Mise à jour 0 : 14 septembre 2018

Le texte sur les pdfs créés par cette approche ne sera pas sélectionnable. Si c'est une exigence, vous trouverez peut-être que cet article utile.

2 votes

J'ai réussi à créer un fichier PDF à partir d'une page html à l'aide de votre réponse. Mais si nous voulons créer un PDF avec plusieurs pages, que faire ? avec une page de taille A4 avec des marges appropriées de tous les côtés et dans chaque nouvelle page cette marge doit être appliquée. s'il vous plaît aidez-moi son argent. Merci d'avance. J'ai créé une question ici question

0 votes

C'est la même chose. Positionnez votre contenu dans une liste de divs. Chaque div représente une feuille a4 (ajoutez du css au div pour reproduire les marges). Exécutez ensuite la fonction d'impression sur chaque div.

2 votes

@ShivekKhurana mon div change selon la taille de l'écran, mais j'aimerais que le fichier PDF soit toujours le même, sans dépendre de la taille de la fenêtre. Y a-t-il un moyen de faire cela ? Aussi, comment puis-je importer {...css} ?

11voto

Aras Points 389

Vous pouvez utiliser des canevas avec jsPDF

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

 }

et vous div avec la capture d'identité est :

exemple

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

5 votes

Pourquoi avez-vous ajouté une copie de la réponse ?

0 votes

La qualité de l'image n'est pas bonne, et avec le côté a4 une partie du Pdf généré est coupée, ce résultat est le même avec les bibliothèques react-to-pdf et jdpdf.

1 votes

Le seul problème avec cette solution est qu'elle capturera l'image entière de la div mais générera un pdf avec une partie de celle-ci - pas la complète. spécialement quand la div est trop large. la solution facile est de remplacer deux lignes. l'une est la ligne du constructeur jspdf const pdf = new jsPDF('p', 'pt', 'a4', false); et le second sera de coller l'image sur la ligne pdf pdf.addImage(imgData, 'PNG', 0, 0, 600, 0, undefined, false); . si vous voulez ajuster la largeur ou la hauteur de l'image capturée dans le pdf, jouez avec le 5e paramètre de la fonction addImage. Devenez fous :P

5voto

Rodrigo Alencar Points 79

Vous pouvez utiliser ReactDOMServer pour rendre votre composant en HTML et l'utiliser ensuite sur jsPDF.

Faites d'abord les importations :

import React from "react";
import ReactDOMServer from "react-dom/server";
import jsPDF from 'jspdf';

alors :

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

Préférez utiliser :

renderToStaticMarkup

au lieu de :

renderToString

Comme les premiers incluent du code HTML sur lequel react s'appuie.

2 votes

Cela semble intéressant, mais renderToStaticMarkup ne renvoie aucun style, donc pour autant que je puisse dire, jsPDF rend simplement une mise en page par défaut du html brut, à moins qu'il y ait un moyen d'injecter des classes css ?

3voto

Tech1337 Points 1

Cette façon de faire peut ou non être sous-optimale, mais la solution la plus simple au problème des pages multiples que j'ai trouvée est de s'assurer que tout le rendu est effectué 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 à rendre à -9999px de la page gauche, Cela n'affecte pas la mise en page et permet à l'élément d'être visible pour html2pdf, en particulier lors de l'utilisation d'onglets, d'accordéons et d'autres composants de l'interface utilisateur qui dépendent de l'affichage de la page. {display: none} .

Cette méthode enveloppe les conditions préalables dans une promesse et appelle pdf.save() dans le finally() méthode. Je ne peux pas être sûr que cette méthode soit infaillible, ni qu'elle soit un anti-modèle, mais il semblerait qu'elle fonctionne dans la plupart des cas où je l'ai utilisée.

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

2voto

Jomin George Paul Points 237
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")

0 votes

@Felix la chaîne HTML permet <style> Il faut donc écrire les CSS en ligne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X