Genera pdf da HTML in div utilizzando Javascript


232

Ho il seguente codice html:

<!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>

Tutto quello che voglio fare è stampare in pdf qualunque cosa si trovi nel div con un ID di "pdf". Questo deve essere fatto usando JavaScript. Il documento "pdf" dovrebbe quindi essere scaricato automaticamente con un nome file di "foobar.pdf"

Sto usando jspdf per fare questo, ma l'unica funzione che ha è "testo" che accetta solo valori di stringa. Voglio inviare HTML a jspdf, non al testo.


1
Come accennato in precedenza, non voglio utilizzare la funzione "testo". Voglio dargli HTML. Il tuo link si occupa solo di testo semplice e non html
John Crawford,

2
jsPDF ha una fromHTMLfunzione; vedi l'esempio "HTML Renderer" su: mrrio.github.io/jsPDF
mg1075

Risposte:


228

jsPDF è in grado di utilizzare plugin. Per abilitarlo a stampare HTML, devi includere alcuni plugin e quindi devi fare quanto segue:

  1. Vai su https://github.com/MrRio/jsPDF e scarica l'ultima versione.
  2. Includi i seguenti script nel tuo progetto:
    • jspdf.js
    • jspdf.plugin.from_html.js
    • jspdf.plugin.split_text_to_size.js
    • jspdf.plugin.standard_fonts_metrics.js

Se si desidera ignorare determinati elementi, è necessario contrassegnarli con un ID, che è quindi possibile ignorare in un gestore di elementi speciali di jsPDF. Pertanto il tuo HTML dovrebbe apparire così:

<!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>

Quindi si utilizza il seguente codice JavaScript per aprire il PDF creato in 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");

Per me questo ha creato un PDF bello e ordinato che includeva solo la riga "stampa questo in pdf".

Si noti che i gestori di elementi speciali trattano solo gli ID nella versione corrente, che è anche indicata in un problema GitHub . Afferma:

Poiché la corrispondenza viene eseguita su ogni elemento nella struttura del nodo, il mio desiderio era di renderlo il più veloce possibile. In quel caso, significava "Solo gli ID degli elementi sono abbinati" Gli ID degli elementi sono ancora eseguiti nello stile jQuery "#id", ma ciò non significa che tutti i selettori jQuery siano supportati.

Pertanto la sostituzione di "#ignorePDF" con selettori di classe come ".ignorePDF" non ha funzionato per me. Dovrai invece aggiungere lo stesso gestore per ogni singolo elemento, che desideri ignorare come:

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

Dagli esempi si afferma anche che è possibile selezionare tag come 'a' o 'li'. Questo potrebbe essere un po 'troppo restrittivo per la maggior parte dei casi:

Supportiamo gestori di elementi speciali. Registrarli con il selettore ID in stile jQuery per ID o nome nodo. ("#iAmID", "div", "span" ecc.) Al momento non è supportato nessun altro tipo di selettore (classe, composto).

Una cosa molto importante da aggiungere è che perdi tutte le informazioni di stile (CSS). Fortunatamente jsPDF è in grado di formattare bene h1, h2, h3 ecc., Il che era abbastanza per i miei scopi. Inoltre stamperà solo testo all'interno di nodi di testo, il che significa che non stamperà i valori di textareas e simili. Esempio:

<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>

3
Immagino che il gestore di elementi possa essere una classe? Sarebbe molto più semanticamente in linea con gli standard HTML5. Un ID non ha solo un peso eccessivo in termini di specificità nei CSS, ma deve anche essere unico.
Imperativo

No, per quanto ne so, le classi non sono al momento selettori validi come indicato nei loro esempi: "supportiamo gestori di elementi speciali. Registrarli con il selettore ID in stile jQuery per ID o nome nodo. (" #IAmID ", "div", "span" ecc.) Al momento non è supportato nessun altro tipo di selettore (classe, composto). ". Tuttavia, è possibile utilizzare i nomi dei tag se questo è in linea con il caso d'uso e non nasconde altri elementi importanti sulla pagina.
snrlx,

Sulla loro pagina parall.ax/products/jspdf dichiarano di supportare IE6 + tramite uno spessore. Non l'ho provato da solo.
snrlx,

2
@snrlx Ottengo pdf vuoto e questo errore: renderer.pdf.sHashCode non è una funzione
Lisa Solomon

5
"Se vuoi ignorare determinati elementi, devi contrassegnarli con un ID" - una libreria meravigliosa, rovinata da quel reqiurement invertito. L'OP ha voluto stampare un singolo <div>, che potrebbe essere uno dei centinaia, quindi deve contrassegnare tutti gli elementi DOM indesiderati ??
Mawg dice di reintegrare Monica il

53

Questa è la soluzione semplice. Questo funziona per me. Puoi usare il concetto di stampa javascript e salvarlo semplicemente come 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>

27
"e semplicemente salvalo come pdf" - Ho perso quella parte. Come si fa?
Mawg dice di reintegrare Monica il

9
Questo ha funzionato per me, per risolvere il problema dello stile CSS, ho creato un altro file css chiamato printPDF.css e aggiunto usando il tag link proprio come nell'esempio sopra: printWindow.document.write ('<html> <head> <title> Contenuti DIV </title> '); printWindow.document.write ('<link rel = "stylesheet" href = "../ css / printPDF.css" />'); printWindow.document.write ('</head> <body>');
Prime_Coder

2
Alcuni commenti: 1) Non è necessario un foglio di stile specifico per la stampa. Nel tuo foglio di stile corrente, fai qualcosa del tipo: @media print {.pageBreak {page-break-before: always; } .labelPdf {font-weight: grassetto; dimensione carattere: 20px; } .noPrintPdf {display: none; }} e utilizzare queste classi come esigenze. 2) .live ("click", ...) non funziona per me, quindi uso invece .on ("click", ...).
Davidson Lima,

1
@DavidsonLima questo codice crea una nuova finestra che non vedrà il vecchio CSS della finestra. Ecco perché sta "ignorando" i CSS, non sta ignorando, solo in una nuova finestra non è caricato. Basta caricarlo in testa e sarà il rendering.
Lukas Liesis,

1
Nel caso in cui qualcuno stia cercando di farlo accadere con Angular, inserisci il tuo file CSS nella cartella delle risorse.
rgantla,

16

È possibile utilizzare autoPrint () e impostare l'output su 'dataurlnewwindow' in questo modo:

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
}

1
Sono curioso, ha mai funzionato per qualcuno diverso da OP? Dalla lettura del codice, mi sembra di capire che funzionerebbe solo con elementi che hanno un ID. Probabilmente è un po 'più complicato di così, comunque non ho idea di come farlo funzionare.
Michael,

14
Da quanto ho osservato, ironicamente, fromHTML funziona solo se l'elemento inviato come parametro non contiene alcun HTML: è supportato solo il testo normale. Kinda uccide lo scopo dell'intera faccenda.
Michael,

Ha funzionato perfettamente per me. L'elemento che si desidera trasferire non deve necessariamente avere un ID. Questo è solo il modo in cui il replicante ha trovato il nodo in cui voleva passare. Inoltre, questa soluzione funziona anche senza "printDoc.autoPrint ()". Se si desidera lasciare questa riga specifica nel codice, è necessario includere il plug-in autoPrint.
snrlx,

13

se hai bisogno di scaricare il pdf di una pagina specifica basta aggiungere un pulsante come questo

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

usa window.print () per stampare tutta la tua pagina non solo un div


2
Solo una semplice aggiunta ad esso, se vuoi creare un pdf scaricabile di un iframe, usa la console per sviluppatori: document.querySelector("#myIframe").contentWindow.print()
ioCron

11

Come accennato, dovresti usare jsPDF e html2canvas . Ho anche trovato una funzione all'interno dei numeri di jsPDF che divide automaticamente il tuo pdf in più pagine ( fonti )

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

3
non converte le immagini
Probosckie

1
grazie per la risposta, potresti dare qualche suggerimento su come metterlo in formato pagina A4?
johannesMatevosyan,

3
Questo in realtà non crea un buon pdf vettoriale, crea molte bitmap con canvas e le impila come immagini. Il risultato ha molti svantaggi - di grandi dimensioni, di bassa qualità, non è possibile copiare e incollare dal PDF, ecc ...
Romano Zenka

6

Un modo è usare la funzione window.print (). Che non richiede alcuna libreria

Professionisti

1.Nessuna libreria esterna richiesta.

2. Possiamo stampare anche solo parti selezionate del corpo.

3.Nessun conflitto css e problemi js.

4. Funzionalità html / js core

--- Basta aggiungere il codice qui sotto

CSS a

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

Codice JS - Chiama la funzione bewlow al clic btn

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

Nota: utilizzare! Important in ogni oggetto css

Esempio -

.legend  {
  background: #9DD2E2 !important;
}

Ci sono problemi con la funzione Stampa dei browser. Gli utenti di solito hanno le opzioni predefinite selezionate per la vista Stampa (margini, dimensioni della pagina e altro). Pertanto, è molto difficile produrre il PDF richiesto con lo stile richiesto senza l'addestramento dell'utente, che è molto più difficile e quasi impossibile ...
Rahmat Ali,

4

uso jspdf e html2canvas per il rendering css ed esporto contenuto di div specifici in quanto questo è il mio codice

$(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);

        });
});

funziona ma converte il contenuto in immagine
Samad,

Inoltre, come impostare l'interruzione di pagina in modo che il contenuto / l'immagine venga stampato su una nuova pagina se non si adatta alla pagina corrente?
SHEKHAR SHETE

4
  • Nessuna dipendenza, JS puro
  • Per aggiungere CSS o immagini, non utilizzare URL relativi, utilizzare URL completi http://...domain.../path.csso simili . Crea un documento HTML separato e non ha alcun contesto di cosa principale.
  • puoi anche incorporare immagini come base64

Questo mi ha servito per anni ormai:

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

1
Il problema è che il pdf non avrà alcun effetto CSS in esso.
Shadab Faiz

@ShadabFaiz Lo sarà, ma potrebbe non essere lo stesso della finestra principale. Puoi ancora aggiungere css personalizzati anche a questo html.
Lukas Liesis

Si. In accordo con te puoi aggiungere CSS nella finestra ma come stampare il risultato nel file pdf?
Mirko Cianfarani,

1
Tuttavia, non esegue il rendering delle immagini.
Jan Pi,

1
Amo questo! Alcune modifiche qua e là e sembra buono. E una piccola cosa non toglie la spaziatura aggiuntiva a <body >questo ha bisogno di questo: P
Phil Roggenbuck,

2

Se vuoi esportare una tabella, puoi dare un'occhiata a questo esempio di esportazione fornito dal widget Griglia dell'interfaccia utente di Shield.

Viene fatto estendendo la configurazione in questo modo:

...
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" }
            ]
        }
    }
}
...

La funzionalità "Esporta in PDF" ha prodotto un documento PDF vuoto.
Richard Nalezynski,

Probabilmente configurazione errata da parte tua ... Se vuoi, pubblica una domanda con il tag shieldui
Vladimir Georgiev,

2

Usa pdfMake.js e questo Gist .

(Ho trovato il Gist qui insieme a un link al pacchetto html-to-pdfmake , che finisco per non usare per ora.)

Dopo aver npm install pdfmakesalvato Gist e htmlToPdf.jslo uso in questo modo:

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

Osservazioni:

  • Il mio caso d'uso è quello di creare l'html pertinente da un documento markdown (con markdown-it ) e successivamente generare il pdf e caricare il suo contenuto binario (che posso ottenere con pdfMakela getBuffer()funzione), tutto dal browser. Il pdf generato risulta essere più bello per questo tipo di HTML rispetto ad altre soluzioni che ho provato.
  • Sono insoddisfatto dei risultati che ho ottenuto jsPDF.fromHTML()suggerito nella risposta accettata, poiché quella soluzione viene facilmente confusa da caratteri speciali nel mio HTML che apparentemente vengono interpretati come una sorta di markup e rovinano totalmente il PDF risultante.
  • L'uso di soluzioni basate su tela (come la jsPDF.from_html()funzione deprecata , da non confondere con quella della risposta accettata) non è un'opzione per me poiché voglio che il testo nel PDF generato sia incollabile, mentre le soluzioni basate su tela generano PDF basati su bitmap.
  • Markdown diretto ai convertitori pdf come md-to-pdf sono solo lato server e non funzionerebbe per me.
  • L'uso della funzionalità di stampa del browser non funzionerebbe per me poiché non voglio visualizzare il PDF generato ma caricare il suo contenuto binario.

Se leggo correttamente il codice, questo non supporta gli stili di bordo CSS (ad es. Sulle tabelle), giusto?
ninjagecko,

1

Sono stato in grado di ottenere jsPDF per stampare tabelle create dinamicamente da 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");
    });
    });
});

Funziona alla grande con Chrome e Firefox ... la formattazione è esplosa in IE.

Ho anche incluso questi:

<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>

2
puoi pubblicare un esempio di come passi html
Erum,

ma il tuo codice è <script src = " html2canvas.hertzen.com/build/html2canvas.js " > < / > che significa che ha bisogno di Internet?
Erum,

1
@Erum puoi scaricare il File.
Eduardo Cuomo,

0

Per acquisire div in PDF è possibile utilizzare la soluzione https://grabz.it . Ha un'API JavaScript che è facile e flessibile e ti permetterà di catturare i contenuti di un singolo elemento HTML come un div o un intervallo

Al fine di attuare ti sarà necessario ottenere prima una chiave di app e segreta e scaricare il (gratuito) SDK.

E ora un esempio.

Supponiamo che tu abbia l'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>

Per acquisire ciò che è sotto l'ID funzionalità dovrai:

//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>

Si prega di notare il target: #feature. #featuresei il selettore CSS, come nell'esempio precedente. Ora, quando la pagina viene caricata, verrà creato uno screenshot di immagine nella stessa posizione del tag script, che conterrà tutto il contenuto delle funzionalità div e nient'altro.

Ci sono altre configurazioni e personalizzazioni che puoi fare al meccanismo div-screenshot, per favore dai un'occhiata qui

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.