jspdf - Gerar PDF a partir de HTML em div usando Javascript
Eu tenho o seguinte código 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>
Tudo o que eu quero fazer é imprimir em PDF tudo o que for encontrado na div com o id "pdf". Isso deve ser feito usando JavaScript. O documento "pdf" deve então ser automaticamente baixado com o nome de arquivo "foobar.pdf"
Tenho usado o jspdf para fazer isso, mas a única função que ele tem é "text", que aceita apenas valores de string. Eu quero enviar HTML para o jspdf, não texto.
Resposta #1
jsPDF é capaz de usar plugins. Para habilitá-lo a imprimir HTML, você precisa incluir certos plugins e, portanto, deve fazer o seguinte:
- Vá para https://github.com/MrRio/jsPDF e baixe a versão mais recente.
- Inclua os seguintes Scripts em seu projeto:
- jspdf.js
- jspdf.plugin.de_html.js
- jspdf.plugin.dividir_texto_por_tamanho.js
- jspdf.plugin.métricas_de_fontes_padrão.js
Se você quiser ignorar certos elementos, você precisa marcá-los com um ID, que você pode então ignorar em um manipulador de elementos especial do jsPDF. Portanto, seu HTML deve parecer com isto:
<!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>
Então você usa o seguinte código JavaScript para abrir o PDF criado em um 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");
Para mim, isso criou um PDF bonito e organizado que incluía apenas a linha 'imprima isto em PDF'.
Por favor, note que os manipuladores de elementos especiais lidam apenas com IDs na versão atual, o que também está indicado em uma Issue do GitHub. Está declarado:
Porque a correspondência é feita contra cada elemento na árvore de nós, meu desejo era torná-la o mais rápida possível. Nesse caso, significava "Apenas IDs de elementos são correspondidos". Os IDs dos elementos ainda são feitos no estilo jQuery "#id", mas isso não significa que todos os seletores jQuery são suportados.
Portanto, substituir '#ignorePDF' por seletores de classe como '.ignorePDF' não funcionou para mim. Em vez disso, você terá que adicionar o mesmo manipulador para cada elemento que deseja ignorar, como:
var elementHandler = {
'#ignoreElement': function (element, renderer) {
return true;
},
'#anotherIdToBeIgnored': function (element, renderer) {
return true;
}
};
A partir dos exemplos, também é mencionado que é possível selecionar tags como 'a' ou 'li'. No entanto, isso pode ser um pouco pouco restritivo para a maioria dos casos de uso:
Nós suportamos manipuladores de elementos especiais. Registre-os com seletor de ID no estilo jQuery para ID ou nome do nó. ("#iAmID", "div", "span" etc.) Não há suporte para nenhum outro tipo de seletores (classe ou composto) neste momento.
Uma coisa muito importante a acrescentar é que você perde todas as informações de estilo (CSS). Felizmente, o jsPDF consegue formatar bem h1, h2, h3 etc., o que foi suficiente para meus propósitos. Além disso, ele só irá imprimir texto dentro de nós de texto, o que significa que ele não imprimirá os valores de áreas de texto e similares. Exemplo:
<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="text" value="Please print me, too!">
</div>
</body>
Resposta #2
Esta é a solução simples. Isso funciona para mim. Você pode usar o conceito de impressão do JavaScript e simplesmente salvar isso como 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>
Resposta #3
- Sem dependências, apenas JS
- Para adicionar CSS ou imagens - não use URLs relativas, use URLs completas
http://...dominio.../caminho.cssou algo assim. Isso cria um documento HTML separado e ele não tem contexto do principal. - você também pode incorporar imagens como base64
Isso me serviu por anos agora:
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;
}
Claro que isso abrirá a caixa de diálogo de impressão e o usuário terá que saber que pode selecionar a opção de imprimir em PDF, para obter o PDF. Pode haver uma impressora pré-selecionada e, se o usuário confirmar, este documento pode realmente ser impresso. Para evitar tal situação e fornecer o PDF sem quaisquer extras, você precisa criar o arquivo PDF. Provavelmente no lado do servidor. Você poderia ter uma pequena página HTML apenas com a fatura e convertê-la em arquivo PDF com o Chrome headless. É muito fácil com o puppeteer. Não há necessidade de instalar/configurar o Chrome, basta instalar o pacote npm puppeteer (gerenciado pela equipe do Chrome) e executá-lo. Tenha em mente que isso realmente vai iniciar o Chrome real, apenas sem GUI, então você precisa ter alguma RAM e CPU para isso. A maioria dos servidores lidará bem com tráfego baixo o suficiente. Aqui está um exemplo de código, mas isso deve rodar no BACKEND. Nodejs. Além disso, é uma chamada lenta, é uma chamada que consome muitos recursos. Você não deve executá-la em uma chamada de API, mas, por exemplo, depois que a fatura foi criada - crie o PDF dela e armazene; se o PDF ainda não foi gerado, apenas mostre uma mensagem para tentar novamente em alguns minutos.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://your-domain.com/path-to-invoice', {
waitUntil: 'networkidle2',
});
await page.pdf({ path: 'invoice-file-path.pdf', format: 'a4' });
await browser.close();
})();
Saiba mais aqui: https://pptr.dev/
Resposta #4
se você precisa de um PDF para download de uma página específica, basta adicionar um botão como este
<h4 onclick="window.print();"> Print </h4>
use window.print() para imprimir toda a sua página, não apenas um div
Resposta #5
Como mencionado, você deve usar jsPDF e html2canvas. Também encontrei uma função nos problemas do jsPDF que divide automaticamente seu PDF em várias páginas (fontes)
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');
}
});
}
Resposta #6
Você pode usar autoPrint() e definir a saída como 'dataurlnewwindow' assim:
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
}
Resposta #7
eu uso jspdf e html2canvas para renderização de css e eu exporto o conteúdo de uma div específica assim como este é o meu código
$(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);
});
});
Resposta #8
Uma maneira é usar a função window.print(). Que não requer nenhuma biblioteca
Prós
1. Nenhuma biblioteca externa requerida.
2. Podemos imprimir apenas partes selecionadas do corpo também.
3. Sem conflitos de CSS e problemas de JS.
4. Funcionalidade básica de HTML/JS
---Basta adicionar o código abaixo
CSS para
@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;
}
}
Código JS - Chame a função abaixo ao clicar no botão
$scope.printWindow = function () {
window.print()
}
Observação: Use !important em cada objeto css
Exemplo -
.legend {
background: #9DD2E2 !important;
}
Resposta #9
Resposta 2022:
Para gerar PDF a partir de um elemento HTML e solicitar para salvar o arquivo:
import { jsPDF } from "jsPDF"
function generatePDF() {
const doc = new jsPDF({ unit: 'pt' }) // create jsPDF object
const pdfElement = document.getElementById('pdf') // HTML element to be converted to PDF
doc.html(pdfElement, {
callback: (pdf) => {
pdf.save('MyPdfFile.pdf')
},
margin: 32, // optional: page margin
// optional: other HTMLOptions
})
}
<button onclick="generatePDF()">Save PDF</button>
...
Para visualizar o PDF sem imprimir:
doc.html(pdfElement, {
callback: (pdf) => {
const myPdfData = pdf.output('datauristring')
}
})
<embed type="application/pdf" src={myPdfData} />
...
Para mais opções de HTML:
https://github.com/parallax/jsPDF/blob/master/types/index.d.ts
