Générer un pdf à partir de HTML en div en utilisant Javascript

232

J'ai le code html suivant:

<!DOCTYPE html>
<html>
    <body>
        <p>don't print this to pdf</p>
        <div id="pdf">
            <p><font size="3" color="red">print this to pdf</font></p>
        </div>
    </body>
</html>

Tout ce que je veux faire, c'est imprimer au format pdf tout ce qui se trouve dans la div avec un identifiant "pdf". Cela doit être fait en utilisant JavaScript. Le document "pdf" devrait alors être automatiquement téléchargé avec un nom de fichier "foobar.pdf"

J'utilise jspdf pour ce faire, mais la seule fonction qu'il possède est "text" qui n'accepte que les valeurs de chaîne. Je veux soumettre du HTML à jspdf, pas du texte.

John Crawford
la source
1
Comme mentionné ci - dessus , je ne pas envie d'utiliser la fonction « texte ». Je veux lui donner du HTML. Votre lien ne concerne que le texte brut et non le HTML
John Crawford
2
jsPDF fait avoir une fromHTMLfonction; voir l'exemple "HTML Renderer" sur: mrrio.github.io/jsPDF
mg1075
Essayez le tutoriel jsPDF ici freakyjolly.com/create-multipage-html-pdf-jspdf-html2canvas
Code Spy

Réponses:

228

jsPDF est capable d'utiliser des plugins. Afin de lui permettre d'imprimer du HTML, vous devez inclure certains plugins et devez donc faire ce qui suit:

  1. Accédez à https://github.com/MrRio/jsPDF et téléchargez la dernière version.
  2. Incluez les scripts suivants dans votre projet:
    • jspdf.js
    • jspdf.plugin.from_html.js
    • jspdf.plugin.split_text_to_size.js
    • jspdf.plugin.standard_fonts_metrics.js

Si vous souhaitez ignorer certains éléments, vous devez les marquer avec un ID, que vous pouvez ensuite ignorer dans un gestionnaire d'élément spécial de jsPDF. Par conséquent, votre code HTML devrait ressembler à ceci:

<!DOCTYPE html>
<html>
  <body>
    <p id="ignorePDF">don't print this to pdf</p>
    <div>
      <p><font size="3" color="red">print this to pdf</font></p>
    </div>
  </body>
</html>

Ensuite, vous utilisez le code JavaScript suivant pour ouvrir le PDF créé dans un PopUp:

var doc = new jsPDF();          
var elementHandler = {
  '#ignorePDF': function (element, renderer) {
    return true;
  }
};
var source = window.document.getElementsByTagName("body")[0];
doc.fromHTML(
    source,
    15,
    15,
    {
      'width': 180,'elementHandlers': elementHandler
    });

doc.output("dataurlnewwindow");

Pour moi, cela a créé un PDF agréable et bien rangé qui ne comprenait que la ligne «imprimer ceci en pdf».

Veuillez noter que les gestionnaires d'éléments spéciaux ne traitent que les ID dans la version actuelle, ce qui est également indiqué dans un problème GitHub . Il est dit:

Parce que l'appariement est fait contre chaque élément de l'arborescence des nœuds, mon désir était de le faire le plus rapidement possible. Dans ce cas, cela signifie "Seuls les ID d'élément sont mis en correspondance" Les ID d'élément sont toujours définis dans le style jQuery "#id", mais cela ne signifie pas que tous les sélecteurs jQuery sont pris en charge.

Par conséquent, le remplacement de «#ignorePDF» par des sélecteurs de classe comme «.ignorePDF» n'a pas fonctionné pour moi. Au lieu de cela, vous devrez ajouter le même gestionnaire pour chaque élément, que vous souhaitez ignorer comme:

var elementHandler = {
  '#ignoreElement': function (element, renderer) {
    return true;
  },
  '#anotherIdToBeIgnored': function (element, renderer) {
    return true;
  }
};

À partir des exemples, il est également indiqué qu'il est possible de sélectionner des balises comme «a» ou «li». Cela pourrait être un peu restrictif pour la plupart des cas d'utilisation:

Nous prenons en charge les gestionnaires d'éléments spéciaux. Enregistrez-les avec le sélecteur d'ID de style jQuery pour l'ID ou le nom du nœud. ("#iAmID", "div", "span" etc.) Il n'y a aucun support pour aucun autre type de sélecteurs (classe, de composé) pour le moment.

Une chose très importante à ajouter est que vous perdez toutes vos informations de style (CSS). Heureusement, jsPDF est capable de formater joliment h1, h2, h3 etc., ce qui était suffisant pour mes besoins. De plus, il imprimera uniquement le texte dans les nœuds de texte, ce qui signifie qu'il n'imprimera pas les valeurs des zones de texte et similaires. Exemple:

<body>
  <ul>
    <!-- This is printed as the element contains a textnode -->        
    <li>Print me!</li>
  </ul>
  <div>
    <!-- This is not printed because jsPDF doesn't deal with the value attribute -->
    <input type="textarea" value="Please print me, too!">
  </div>
</body>
snrlx
la source
3
Je vais deviner que le gestionnaire d'élément peut être une classe? Ce serait beaucoup plus sémantiquement conforme aux normes HTML5. Un ID a non seulement un poids de spécificité trop élevé en CSS, mais il doit également être unique.
Impératif
Non, pour autant que je sache, les classes ne sont pas des sélecteurs valides pour le moment, comme indiqué dans leurs exemples: "nous prenons en charge les gestionnaires d'éléments spéciaux. Enregistrez-les avec le sélecteur d'ID de style jQuery pour l'ID ou le nom de nœud." "div", "span", etc.) Il n'y a pas de support pour tout autre type de sélecteurs (classe, de composé) pour le moment. ". Néanmoins, il est possible d'utiliser des noms de balise si cela correspond à votre cas d'utilisation et ne masque pas d'autres éléments importants sur votre page.
snrlx
Sur leur page parall.ax/products/jspdf, ils déclarent qu'ils prennent en charge IE6 + via un shim. Je ne l'ai pas essayé moi-même.
snrlx
2
@snrlx J'obtiens un pdf vierge et cette erreur: renderer.pdf.sHashCode n'est pas une fonction
Lisa Solomon
5
"Si vous voulez ignorer certains éléments, vous devez les marquer avec un ID" - une merveilleuse bibliothèque, ruinée par cette réévaluation inversée. L'OP voulait imprimer un seul <div>, qui pourrait être l'un des centaines - il doit donc marquer tous les éléments DOM indésirables ??
Mawg dit de réintégrer Monica le
53

C'est la solution simple. Cela fonctionne pour moi.Vous pouvez utiliser le concept d'impression javascript et l'enregistrer simplement au format pdf.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript">
        $("#btnPrint").live("click", function () {
            var divContents = $("#dvContainer").html();
            var printWindow = window.open('', '', 'height=400,width=800');
            printWindow.document.write('<html><head><title>DIV Contents</title>');
            printWindow.document.write('</head><body >');
            printWindow.document.write(divContents);
            printWindow.document.write('</body></html>');
            printWindow.document.close();
            printWindow.print();
        });
    </script>
</head>
<body>
    <form id="form1">
    <div id="dvContainer">
        This content needs to be printed.
    </div>
    <input type="button" value="Print Div Contents" id="btnPrint" />
    </form>
</body>
</html>
vaibhav kulkarni
la source
27
"et enregistrez-le simplement au format pdf" - j'ai raté cette partie. Comment faites-vous?
Mawg dit réintégrer Monica le
9
Cela a fonctionné pour moi, pour résoudre le problème du style CSS, j'ai créé un autre fichier css appelé printPDF.css et ajouté en utilisant une balise de lien comme ceci dans l'exemple ci-dessus: printWindow.document.write ('<html> <head> <title> Contenu DIV </title> '); printWindow.document.write ('<link rel = "stylesheet" href = "../ css / printPDF.css" />'); printWindow.document.write ('</head> <body>');
Prime_Coder
2
Quelques commentaires: 1) Vous n'avez pas besoin d'une feuille de style spécifique pour l'impression. Dans votre feuille de style actuelle, faites quelque chose comme: @media print {.pageBreak {page-break-before: always; } .labelPdf {font-weight: bold; taille de police: 20px; } .noPrintPdf {affichage: aucun; }} et utilisez ces classes selon vos besoins. 2) .live ("click", ...) ne fonctionne pas pour moi, donc j'utilise .on ("click", ...) à la place.
Davidson Lima
1
@DavidsonLima ce code crée une nouvelle fenêtre qui ne verra pas l'ancien CSS de la fenêtre. C'est pourquoi c'est "ignorer" css, ce n'est pas ignorer, juste dans une nouvelle fenêtre il n'est pas chargé. Il suffit de le charger dans la tête et il sera rendu.
Lukas Liesis
1
Juste au cas où quelqu'un essaierait de faire cela avec Angular, veuillez mettre votre fichier CSS dans le dossier des ressources.
rgantla
16

Vous pouvez utiliser autoPrint () et définir la sortie sur 'dataurlnewwindow' comme ceci:

function printPDF() {
    var printDoc = new jsPDF();
    printDoc.fromHTML($('#pdf').get(0), 10, 10, {'width': 180});
    printDoc.autoPrint();
    printDoc.output("dataurlnewwindow"); // this opens a new popup,  after this the PDF opens the print window view but there are browser inconsistencies with how this is handled
}
Marco
la source
1
Je suis curieux, cela a-t-il déjà fonctionné pour quelqu'un d'autre que OP? De la lecture du code, je semble comprendre que cela ne fonctionnerait qu'avec des éléments qui ont un ID. C'est probablement un peu plus compliqué que ça, de toute façon je n'ai aucune idée de comment faire fonctionner ça.
Michael
14
D'après ce que j'ai observé, très ironiquement, fromHTML ne fonctionne que si l'élément envoyé en tant que paramètre ne contient pas de HTML: uniquement du texte brut pris en charge. Un peu tue le but de tout ça imo.
Michael
A parfaitement fonctionné pour moi. L'élément que vous souhaitez transmettre n'est pas nécessairement obligé d'avoir une pièce d'identité. C'est exactement de cette façon que le réplicateur a trouvé le nœud qu'il voulait transmettre. De plus, cette solution fonctionne également sans 'printDoc.autoPrint ()'. Si vous souhaitez laisser cette ligne spécifique dans le code, vous devez inclure le plugin autoPrint.
snrlx
13

si vous avez besoin de télécharger le pdf d'une page spécifique, ajoutez simplement un bouton comme celui-ci

<h4 onclick="window.print();"> Print </h4>

utilisez window.print () pour imprimer votre page entière pas seulement un div

Wael Mahmoud
la source
2
Juste un ajout simple, si vous voulez créer un pdf téléchargeable d'un iframe, alors utilisez la console développeur: document.querySelector("#myIframe").contentWindow.print()
ioCron
11

Comme mentionné, vous devez utiliser jsPDF et html2canvas . J'ai également trouvé une fonction dans les problèmes de jsPDF qui divise automatiquement votre pdf en plusieurs pages ( sources )

function makePDF() {

    var quotes = document.getElementById('container-fluid');

    html2canvas(quotes, {
        onrendered: function(canvas) {

        //! MAKE YOUR PDF
        var pdf = new jsPDF('p', 'pt', 'letter');

        for (var i = 0; i <= quotes.clientHeight/980; i++) {
            //! This is all just html2canvas stuff
            var srcImg  = canvas;
            var sX      = 0;
            var sY      = 980*i; // start 980 pixels down for every new page
            var sWidth  = 900;
            var sHeight = 980;
            var dX      = 0;
            var dY      = 0;
            var dWidth  = 900;
            var dHeight = 980;

            window.onePageCanvas = document.createElement("canvas");
            onePageCanvas.setAttribute('width', 900);
            onePageCanvas.setAttribute('height', 980);
            var ctx = onePageCanvas.getContext('2d');
            // details on this usage of this function: 
            // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Slicing
            ctx.drawImage(srcImg,sX,sY,sWidth,sHeight,dX,dY,dWidth,dHeight);

            // document.body.appendChild(canvas);
            var canvasDataURL = onePageCanvas.toDataURL("image/png", 1.0);

            var width         = onePageCanvas.width;
            var height        = onePageCanvas.clientHeight;

            //! If we're on anything other than the first page,
            // add another page
            if (i > 0) {
                pdf.addPage(612, 791); //8.5" x 11" in pts (in*72)
            }
            //! now we declare that we're working on that page
            pdf.setPage(i+1);
            //! now we add content to that page!
            pdf.addImage(canvasDataURL, 'PNG', 20, 40, (width*.62), (height*.62));

        }
        //! after the for loop is finished running, we save the pdf.
        pdf.save('test.pdf');
    }
  });
}
BaptWaels
la source
3
il ne convertit pas les images
Probosckie
1
merci pour la réponse, pourriez-vous donner un indice sur la façon de le mettre au format A4?
johannesMatevosyan
3
Cela ne fait pas vraiment un bon pdf vectoriel, cela fait beaucoup de bitmaps avec canvas et les empile comme des images. Le résultat présente de nombreux inconvénients - grand, de faible qualité, impossible de copier-coller à partir du PDF, etc ...
Roman Zenka
6

Une façon consiste à utiliser la fonction window.print (). Qui ne nécessite aucune bibliothèque

Avantages

Aucune bibliothèque externe requise.

2.Nous pouvons également imprimer uniquement certaines parties du corps.

Pas de conflits css et de problèmes js.

4.Core fonctionnalité html / js

--- Ajoutez simplement le code ci-dessous

CSS à

@media print {
        body * {
            visibility: hidden; // part to hide at the time of print
            -webkit-print-color-adjust: exact !important; // not necessary use         
               if colors not visible
        }

        #printBtn {
            visibility: hidden !important; // To hide 
        }

        #page-wrapper * {
            visibility: visible; // Print only required part
            text-align: left;
            -webkit-print-color-adjust: exact !important;
        }
    }

Code JS - Appelez la fonction bewlow sur un clic btn

$scope.printWindow = function () {
  window.print()
}

Remarque: utilisez! Important dans chaque objet css

Exemple -

.legend  {
  background: #9DD2E2 !important;
}
Rohit Parte
la source
Il y a des problèmes avec la fonction d'impression des navigateurs. Les utilisateurs ont généralement des options par défaut sélectionnées pour la vue d'impression (marges, taille de page, etc.). Par conséquent, il est très difficile de produire le PDF requis avec le style requis sans former l'utilisateur, ce qui est beaucoup plus difficile et presque impossible ...
Rahmat Ali
4

j'utilise jspdf et html2canvas pour le rendu css et j'exporte le contenu de div spécifique car c'est mon code

$(document).ready(function () {
    let btn=$('#c-oreder-preview');
    btn.text('download');
    btn.on('click',()=> {

        $('#c-invoice').modal('show');
        setTimeout(function () {
            html2canvas(document.querySelector("#c-print")).then(canvas => {
                //$("#previewBeforeDownload").html(canvas);
                var imgData = canvas.toDataURL("image/jpeg",1);
                var pdf = new jsPDF("p", "mm", "a4");
                var pageWidth = pdf.internal.pageSize.getWidth();
                var pageHeight = pdf.internal.pageSize.getHeight();
                var imageWidth = canvas.width;
                var imageHeight = canvas.height;

                var ratio = imageWidth/imageHeight >= pageWidth/pageHeight ? pageWidth/imageWidth : pageHeight/imageHeight;
                //pdf = new jsPDF(this.state.orientation, undefined, format);
                pdf.addImage(imgData, 'JPEG', 0, 0, imageWidth * ratio, imageHeight * ratio);
                pdf.save("invoice.pdf");
                //$("#previewBeforeDownload").hide();
                $('#c-invoice').modal('hide');
            });
        },500);

        });
});
ghazaleh javaheri
la source
cela fonctionne mais il convertit le contenu en image
Samad
De plus, comment définir un saut de page pour que le contenu / l'image s'imprime sur une nouvelle page si elle ne tient pas sur la page actuelle?
SHEKHAR SHETE
4
  • Pas de dépendances, pur JS
  • Pour ajouter du CSS ou des images - n'utilisez pas d'URL relatives, utilisez des URL complètes http://...domain.../path.cssou ainsi. Il crée un document HTML séparé et il n'a pas de contexte principal.
  • vous pouvez également incorporer des images en base64

Cela me sert depuis des années maintenant:

export default function printDiv({divId, title}) {
  let mywindow = window.open('', 'PRINT', 'height=650,width=900,top=100,left=150');

  mywindow.document.write(`<html><head><title>${title}</title>`);
  mywindow.document.write('</head><body >');
  mywindow.document.write(document.getElementById(divId).innerHTML);
  mywindow.document.write('</body></html>');

  mywindow.document.close(); // necessary for IE >= 10
  mywindow.focus(); // necessary for IE >= 10*/

  mywindow.print();
  mywindow.close();

  return true;
}
Lukas Liesis
la source
1
Le problème est que le pdf n'aura aucun effet CSS.
Shadab Faiz
@ShadabFaiz Ce sera le cas mais ce ne sera peut-être pas la même chose que la fenêtre principale. Vous pouvez toujours ajouter des CSS personnalisés à ce HTML aussi.
Lukas Liesis
Ouais. Je suis d'accord avec vous pouvez ajouter css dans la fenêtre mais comment imprimer le résultat en fichier pdf?
Mirko Cianfarani
1
Cependant, ne rend pas les images.
Jan Pi
1
J'aime cela! Quelques ajustements ici et là et ça a l'air bien. Et une petite chose ne supprime pas l'espacement supplémentaire dont <body >il a besoin: P
Phil Roggenbuck
2

Si vous souhaitez exporter une table, vous pouvez jeter un œil à cet exemple d'exportation fourni par le widget Shield UI Grid.

Cela se fait en étendant la configuration comme ceci:

...
exportOptions: {
    proxy: "/filesaver/save",
    pdf: {
        fileName: "shieldui-export",
        author: "John Smith",
        dataSource: {
            data: gridData
        },
        readDataSource: true,
        header: {
            cells: [
                { field: "id", title: "ID", width: 50 },
                { field: "name", title: "Person Name", width: 100 },
                { field: "company", title: "Company Name", width: 100 },
                { field: "email", title: "Email Address" }
            ]
        }
    }
}
...
Vladimir Georgiev
la source
La fonctionnalité "Exporter au format PDF" a abouti à un document PDF vide.
Richard Nalezynski
Configuration probablement incorrecte de votre côté ... Si vous voulez, posez une question avec la balise shieldui
Vladimir Georgiev
2

Utilisez pdfMake.js et ce Gist .

(J'ai trouvé le Gist ici avec un lien vers le paquet html-to-pdfmake , que je par ne pas utiliser pour l'instant.)

Après avoir npm install pdfmakesauvegardé le Gist, htmlToPdf.jsje l'utilise comme ceci:

const pdfMakeX = require('pdfmake/build/pdfmake.js');
const pdfFontsX = require('pdfmake-unicode/dist/pdfmake-unicode.js');
pdfMakeX.vfs = pdfFontsX.pdfMake.vfs;
import * as pdfMake from 'pdfmake/build/pdfmake';
import htmlToPdf from './htmlToPdf.js';

var docDef = htmlToPdf(`<b>Sample</b>`);
pdfMake.createPdf({content:docDef}).download('sample.pdf');

Remarques:

  • Mon cas d'utilisation est de créer le code html correspondant d'un document Markdown (avec démarquage-it ) et générer ensuite le pdf, et de télécharger son contenu binaire (que je peux obtenir avec pdfMake« sgetBuffer() fonction de), le tout depuis le navigateur. Le pdf généré s'avère plus agréable pour ce type de code HTML qu'avec d'autres solutions que j'ai essayées.
  • Je ne suis pas satisfait des résultats que j'ai obtenus jsPDF.fromHTML() suggérés dans la réponse acceptée, car cette solution est facilement confondue par des caractères spéciaux dans mon HTML qui sont apparemment interprétés comme une sorte de balisage et gâchent totalement le PDF résultant.
  • Utilisation de solutions basées sur le canevas (comme les obsolètes jsPDF.from_html() fonction , à ne pas confondre avec celle de la réponse acceptée) n'est pas une option pour moi car je veux que le texte du PDF généré soit collable, tandis que les solutions basées sur le canevas génèrent des PDF basés sur des bitmaps.
  • Le démarquage direct aux convertisseurs pdf comme md-to-pdf est côté serveur uniquement et ne fonctionnerait pas pour moi.
  • L'utilisation de la fonctionnalité d'impression du navigateur ne fonctionnerait pas pour moi car je ne veux pas afficher le PDF généré mais télécharger son contenu binaire.
ilmiacs
la source
Si je lis le code correctement, cela ne prend pas en charge les styles de bordure CSS (par exemple sur les tables), n'est-ce pas?
ninjagecko
1

J'ai pu obtenir jsPDF pour imprimer des tables créées dynamiquement à partir d'un div.

$(document).ready(function() {

        $("#pdfDiv").click(function() {

    var pdf = new jsPDF('p','pt','letter');
    var specialElementHandlers = {
    '#rentalListCan': function (element, renderer) {
        return true;
        }
    };

    pdf.addHTML($('#rentalListCan').first(), function() {
        pdf.save("caravan.pdf");
    });
    });
});

Fonctionne très bien avec Chrome et Firefox ... le formatage est entièrement explosé dans IE.

J'ai également inclus ceux-ci:

<script src="js/jspdf.js"></script>
    <script src="js/jspdf.plugin.from_html.js"></script>
    <script src="js/jspdf.plugin.addhtml.js"></script>
    <script src="//mrrio.github.io/jsPDF/dist/jspdf.debug.js"></script>
    <script src="http://html2canvas.hertzen.com/build/html2canvas.js"></script>
    <script type="text/javascript" src="./libs/FileSaver.js/FileSaver.js"></script>
    <script type="text/javascript" src="./libs/Blob.js/Blob.js"></script>
    <script type="text/javascript" src="./libs/deflate.js"></script>
    <script type="text/javascript" src="./libs/adler32cs.js/adler32cs.js"></script>

    <script type="text/javascript" src="js/jspdf.plugin.addimage.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.sillysvgrenderer.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.split_text_to_size.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.standard_fonts_metrics.js"></script>
Wasskinny
la source
2
can u pls post example how u pass html
Erum
mais votre code est <script src = " html2canvas.hertzen.com/build/html2canvas.js " > </ > ce qui signifie qu'il a besoin d'Internet?
Erum
1
@Erum, vous pouvez télécharger le fichier.
Eduardo Cuomo
0

Pour capturer div en PDF, vous pouvez utiliser la solution https://grabz.it . Il dispose d'une API JavaScript simple et flexible qui vous permettra de capturer le contenu d'un seul élément HTML tel qu'un div ou un span

Pour le mettre en œuvre, vous devrez d'abord obtenir une clé d'application et un secret et télécharger le SDK (gratuit).

Et maintenant un exemple.

Disons que vous avez le HTML:

<div id="features">
    <h4>Acme Camera</h4>
    <label>Price</label>$399<br />
    <label>Rating</label>4.5 out of 5
</div>
<p>Cras ut velit sed purus porttitor aliquam. Nulla tristique magna ac libero tempor, ac vestibulum felisvulput ate. Nam ut velit eget
risus porttitor tristique at ac diam. Sed nisi risus, rutrum a metus suscipit, euismod tristique nulla. Etiam venenatis rutrum risus at
blandit. In hac habitasse platea dictumst. Suspendisse potenti. Phasellus eget vehicula felis.</p>

Pour capturer ce qui se trouve sous l'ID des fonctionnalités, vous devrez:

//add the sdk
<script type="text/javascript" src="grabzit.min.js"></script>
<script type="text/javascript">
//login with your key and secret. 
GrabzIt("KEY", "SECRET").ConvertURL("http://www.example.com/my-page.html",
{"target": "#features", "format": "pdf"}).Create();
</script>

Veuillez noter le target: #feature. #featureest votre sélecteur CSS, comme dans l'exemple précédent. Maintenant, lorsque la page est chargée, une capture d'écran d'image sera désormais créée au même emplacement que la balise de script, qui contiendra tout le contenu de la fonctionnalité div et rien d'autre.

Voici d' autres configurations et personnalisations que vous pouvez faire pour le mécanisme de capture d'écran div, veuillez les vérifier ici

Johnny
la source