Pesquisa de site

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:

  1. Vá para https://github.com/MrRio/jsPDF e baixe a versão mais recente.
  2. 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.css ou 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

Artigos relacionados