Gere pdf a partir de HTML em div usando Javascript

232

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 o que for encontrado na div com o ID "pdf". Isso deve ser feito usando JavaScript. O documento "pdf" deve ser baixado automaticamente com o nome de arquivo "foobar.pdf"

Eu tenho usado jspdf para fazer isso, mas a única função que ele tem é "texto", que aceita apenas valores de seqüência de caracteres. Quero enviar HTML para jspdf, não texto.

John Crawford
fonte
1
Como mencionado acima, não quero usar a função "texto". Eu quero dar HTML. O seu link lida apenas com texto simples e não html
John Crawford
2
jsPDF faz ter uma fromHTMLfunção; veja o exemplo "HTML Renderer" em: mrrio.github.io/jsPDF
mg1075
Tente o tutorial do jsPDF aqui freakyjolly.com/create-multipage-html-pdf-jspdf-html2canvas
Código Spy

Respostas:

228

O jsPDF pode usar plugins. Para permitir a impressão de HTML, você deve incluir determinados plugins e, portanto, fazer o seguinte:

  1. Acesse https://github.com/MrRio/jsPDF e baixe a versão mais recente.
  2. Inclua os seguintes scripts no seu projeto:
    • jspdf.js
    • jspdf.plugin.from_html.js
    • jspdf.plugin.split_text_to_size.js
    • jspdf.plugin.standard_fonts_metrics.js

Se você deseja ignorar certos elementos, é necessário marcá-los com um ID, que pode ser ignorado em um manipulador de elemento especial do jsPDF. Portanto, seu HTML deve ficar assim:

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

Em seguida, use 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 agradável e arrumado, que incluía apenas a linha 'imprima isso em pdf'.

Observe que os manipuladores de elementos especiais lidam apenas com IDs na versão atual, o que também é declarado em um problema do GitHub . Afirma:

Como a correspondência é feita com todos os elementos da árvore de nós, meu desejo era torná-lo o mais rápido possível. Nesse caso, significava "Somente IDs de elemento são correspondidos". Os IDs de elemento ainda são feitos no estilo jQuery "#id", mas isso não significa que todos os seletores de jQuery sejam suportados.

Portanto, substituir '#ignorePDF' por seletores de classe como '.ignorePDF' não funcionou para mim. Em vez disso, você precisará 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 é afirmado que é possível selecionar tags como 'a' ou 'li'. Isso pode ser um pouco irrestrito para a maioria dos casos de uso:

Apoiamos manipuladores de elementos especiais. Registre-os no seletor de ID no estilo jQuery para o ID ou o nome do nó. ("#iAmID", "div", "span" etc.) Não há suporte para nenhum outro tipo de seletor (classe, composto) no momento.

Uma coisa muito importante a acrescentar é que você perde todas as suas 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 imprimirá apenas texto dentro de nós de texto, o que significa que 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="textarea" value="Please print me, too!">
  </div>
</body>
snrlx
fonte
3
Vou adivinhar que o manipulador de elementos pode ser uma classe? Isso seria muito mais semanticamente alinhado com os padrões HTML5. Um ID não apenas carrega muito peso específico em CSS, mas também precisa ser único.
imperativo
Não, até onde eu sei, as classes não são seletores válidos no momento, como indicado em seus exemplos: "suportamos manipuladores de elementos especiais. Registre-os no seletor de ID no estilo jQuery para o ID ou o nome do nó. (" #IAmID ", "div", "span" etc.) Não há suporte para nenhum outro tipo de seletor (classe, composto) no momento. ". Ainda assim, é possível usar nomes de tags se isso estiver alinhado com o seu caso de uso e não ocultar outros elementos importantes em sua página.
snrlx
Na página parall.ax/products/jspdf, eles afirmam que suportam o IE6 + por meio de um calço. Ainda não tentei.
snrlx
2
@snrlx eu recebo pdf em branco e este erro: renderer.pdf.sHashCode não é uma função
Lisa Solomon
5
"Se você deseja ignorar certos elementos, precisa marcá-los com um ID" - uma biblioteca maravilhosa, arruinada por essa re-medição invertida. O OP queria imprimir uma única <div>, que poderia ser uma das centenas - então ele tem que marcar todos os elementos DOM indesejados ?
Mawg diz que restaura Monica
53

Esta é a solução simples. Isso funciona para mim.Você pode usar o conceito de impressão em javascript e salvá-lo 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>
vaibhav kulkarni
fonte
27
"e simples salvar isso em pdf" - eu perdi essa parte. Como você faz isso?
Mawg diz que restabelece Monica
9
Isso funcionou para mim. Para resolver o problema do estilo CSS, criei outro arquivo css chamado printPDF.css e o adicionei usando a tag link como esta no exemplo acima: printWindow.document.write ('<html> <head> <title> Conteúdo da DIV </title> '); printWindow.document.write ('<link rel = "stylesheet" href = "../ css / printPDF.css" />'); printWindow.document.write ('</head> <body>');
Prime_Coder 16/02
2
Alguns comentários: 1) Você não precisa de uma folha de estilo específica para impressão. Na sua folha de estilo atual, faça algo como: @media print {.pageBreak {page-break-before: always; } .labelPdf {font-weight: bold; tamanho da fonte: 20 px; } .noPrintPdf {display: none; }} e use essas classes conforme suas necessidades. 2) .live ("clique", ...) não funciona para mim, então eu uso .on ("clique", ...).
Davidson Lima
1
@ DavidsonLima esse código cria uma nova janela que não verá a janela antiga css. É por isso que está "ignorando" o css, não está ignorando, apenas na nova janela não está carregado. Basta carregá-lo na cabeça e ele será renderizado.
Lukas Liesis
1
Caso alguém esteja tentando fazer isso acontecer com o Angular, coloque seu arquivo CSS na pasta assets.
Rogantla
16

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
}
Marco
fonte
1
Estou curioso, isso já funcionou para alguém que não seja OP? Ao ler o código, pareço entender que ele funcionaria apenas com elementos que têm um ID. Provavelmente é um pouco mais complicado que isso, de qualquer forma, não faço ideia de como fazer isso funcionar.
Michael
14
Pelo que observei, muito ironicamente, fromHTML só funciona se o elemento enviado como parâmetro não contiver HTML: apenas texto simples suportado. Kinda mata o propósito da coisa toda imo.
Michael
Funcionou perfeitamente para mim. O elemento que você deseja transmitir não precisa necessariamente de um ID. Foi assim que o replicante encontrou o nó que ele queria passar. Além disso, esta solução também funciona sem 'printDoc.autoPrint ()'. Se você quiser deixar essa linha específica no código, será necessário incluir o autoPrint-Plugin.
181114 snrlx
13

Se você precisar fazer o download de PDF de uma página específica, basta adicionar um botão como este

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

use window.print () para imprimir sua página inteira e não apenas uma div

Wael Mahmoud
fonte
2
Apenas uma simples adição a isso, se você quer criar um PDF para download de um iframe, em seguida, usar o console do desenvolvedor: document.querySelector("#myIframe").contentWindow.print()
ioCron
11

Como mencionado, você deve usar jsPDF e html2canvas . Também encontrei uma função dentro dos problemas do jsPDF que divide automaticamente o 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');
    }
  });
}
BaptWaels
fonte
3
ele não converte imagens
Probosckie
1
obrigado pela resposta, você poderia dar alguma dica sobre como colocá-lo no formato de página A4?
johannesMatevosyan
3
Isso realmente não é um bom vetor de pdf, ele cria muitos bitmaps com tela e os empilha como imagens. O resultado tem muitas desvantagens - grande, de baixa qualidade, não pode copiar e colar a partir do PDF, etc ...
Roman Zenka
6

Uma maneira é usar a função window.print (). O que não requer nenhuma biblioteca

Prós

1. Nenhuma biblioteca externa requer.

2.We pode imprimir somente partes selecionadas do corpo também.

Conflitos 3.SS do css e edições do js.

Funcionalidade 4.Core 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 bewlow ao clicar em btn

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

Nota: Use! Important em todos os objetos css

Exemplo -

.legend  {
  background: #9DD2E2 !important;
}
Rohit Parte
fonte
Há problemas com a função Imprimir dos navegadores. Os usuários geralmente têm opções padrão selecionadas para a visualização Imprimir (margens, tamanho da página e mais). Portanto, é muito difícil produzir PDF necessário com styling necessário, sem treinamento de usuários, o que é muito mais difícil e aprox impossível ...
Rahmat Ali
4

eu uso jspdf e html2canvas para renderização css e exporto conteúdo de div específico, pois esse é 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);

        });
});
ghazaleh javaheri
fonte
este trabalho mas converter o conteúdo para a imagem
Samad
Além disso, como definir a quebra de página para que o conteúdo / imagem seja impresso em uma nova página se não couber na página atual?
SHEKHAR SHETE
4
  • Sem dependências, JS puro
  • Para adicionar CSS ou imagens - não use URLs relativos, use URLs completos http://...domain.../path.cssou mais. Ele cria um documento HTML separado e não tem contexto da coisa 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;
}
Lukas Liesis
fonte
1
O problema é que o pdf não terá efeitos CSS.
Shadab Faiz
@ShadabFaiz Será, mas pode não ser o mesmo da janela principal. Você ainda pode adicionar css personalizado a este html também.
Lukas Liesis
Sim. Concordo que você pode adicionar css na janela, mas como imprimir o resultado no arquivo pdf?
Mirko Cianfarani
1
Não renderiza imagens, no entanto.
Jan Pi
1
Eu amo isto! Alguns ajustes aqui e ali e parece bom. E uma pequena coisa não remove o espaçamento extra <body >necessário: P
Phil Roggenbuck
2

Se você deseja exportar uma tabela, pode dar uma olhada nesta amostra de exportação fornecida pelo widget Grade UI do Shield.

Isso é feito estendendo a configuração da seguinte maneira:

...
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
fonte
A funcionalidade "Exportar para PDF" resultou em um documento PDF vazio.
Richard Nalezynski
Provavelmente configuração errada do seu lado ... Se você quiser, postar uma pergunta com a tag shieldui
Vladimir Georgiev
2

Use pdfMake.js e este Gist .

(Encontrei o Gist aqui junto com um link para o pacote html-to-pdfmake , que acabo não usando no momento.)

Depois de npm install pdfmakesalvar o Gist htmlToPdf.js, uso-o assim:

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

Observações:

  • Meu caso de uso é criar o html relevante a partir de um documento de remarcação (com remarcação ) e, subsequentemente, gerar o pdf e fazer o upload do seu conteúdo binário (que posso obter com pdfMakea getBuffer()função), tudo a partir do navegador. O pdf gerado acaba sendo mais agradável para esse tipo de html do que com outras soluções que tentei.
  • Estou insatisfeito com os resultados que obtive jsPDF.fromHTML()sugeridos na resposta aceita, pois essa solução é facilmente confundida por caracteres especiais no meu HTML que aparentemente são interpretados como uma espécie de marcação e estragam totalmente o PDF resultante.
  • Usar soluções baseadas em tela (como a jsPDF.from_html()função descontinuada , que não deve ser confundida com a da resposta aceita) não é uma opção para mim, pois quero que o texto no PDF gerado seja pastável, enquanto as soluções baseadas em tela geram PDFs baseados em bitmap.
  • A redução direta para conversores de pdf como md-para-pdf é apenas do lado do servidor e não funcionaria para mim.
  • Usar a funcionalidade de impressão do navegador não funcionaria para mim, pois não quero exibir o PDF gerado, mas carregar o conteúdo binário.
ilmiacs
fonte
Se eu li o código corretamente, isso não suporta estilos de borda CSS (por exemplo, em tabelas), correto?
Ninjagecko 26/10/19
1

Consegui que o jsPDF imprimisse tabelas criadas dinamicamente a partir de uma 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");
    });
    });
});

Funciona muito bem com o Chrome e o Firefox ... a formatação é ampliada no IE.

Eu também incluí estes:

<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
fonte
2
você pode pls postar exemplo como você passa html #
30515
mas seu código é <script src = " html2canvas.hertzen.com/build/html2canvas.js " > </… >, o que significa que ele precisa de internet?
Erum
1
@Erum você pode baixar o arquivo.
Eduardo Cuomo
0

Para capturar div como PDF, você pode usar a solução https://grabz.it . Possui uma API JavaScript que é fácil e flexível e permite capturar o conteúdo de um único elemento HTML, como div ou span

Para implementá-lo, você precisa primeiro obter uma chave e um segredo do aplicativo e fazer o download do SDK (gratuito).

E agora um exemplo.

Digamos que você tenha o 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>

Para capturar o que está sob o ID dos recursos, você precisará:

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

Observe o target: #feature. #featureé o seu seletor de CSS, como no exemplo anterior. Agora, quando a página for carregada, uma captura de tela da imagem será criada no mesmo local que a tag de script, que conterá todo o conteúdo dos recursos div e nada mais.

Existem outras configurações e customizações que você pode fazer com o mecanismo de captura de tela div, confira aqui

Johnny
fonte