9 minutes
Gerando Pdfs Em Blazor Webassembly Com Jspdf E Html2canvas
Introdução
Ao projetar e desenvolver aplicações web modernas, frequentemente nos deparamos com a necessidade de gerar documentos PDF a partir de conteúdo HTML. Seja para fornecer relatórios, recibos, ou simplesmente para permitir que os usuários salvem informações em um formato portátil e amplamente aceito, os PDFs se mostram um recurso indispensável.
No entanto, ao desenvolver aplicações com Blazor WebAssembly, uma estrutura emergente que oferece uma experiência de programação semelhante a uma aplicação cliente/servidor no navegador, a implementação dessa funcionalidade pode apresentar desafios. A interação entre a linguagem C# utilizada no Blazor e as bibliotecas JavaScript comumente utilizadas para a geração de PDFs, como jsPDF e html2canvas, pode ser complicada.
Neste artigo, vou abordar esses desafios e apresentar uma solução robusta para a geração de PDFs a partir de páginas web utilizando Blazor WebAssembly, jsPDF e html2canvas. Meu objetivo é compartilhar a solução encontrada através da minha experiência, oferecendo um recurso valioso para outros profissionais que possam estar lidando com desafios semelhantes em seus próprios projetos.
Conhecendo as Ferramentas
Blazor WebAssembly
O Blazor WebAssembly é uma estrutura de desenvolvimento de aplicações web desenvolvida pela Microsoft. Essa estrutura permite que os desenvolvedores construam aplicações web interativas utilizando C# e .NET, em vez das tradicionais linguagens de programação de front-end, como JavaScript. Com o Blazor WebAssembly, o código .NET é executado diretamente no navegador, graças à tecnologia WebAssembly, permitindo uma experiência de programação mais unificada e uma maior reutilização do código.
jsPDF
O jsPDF é uma biblioteca JavaScript popular para a geração de arquivos PDF do lado do cliente. É uma ferramenta eficiente e flexível que pode transformar dados de várias fontes, incluindo HTML, em documentos PDF formatados. Com uma API simples e extensa, os desenvolvedores podem usar jsPDF para criar documentos PDF complexos e personalizados diretamente no navegador do usuário.
html2canvas
html2canvas é outra biblioteca JavaScript, projetada para capturar screenshots de páginas web ou partes delas, diretamente no navegador do usuário. O html2canvas funciona “pintando” o conteúdo HTML em um elemento canvas, que então pode ser transformado em uma imagem estática em vários formatos, incluindo PNG. Isso o torna uma ferramenta valiosa quando precisamos converter conteúdo HTML em uma imagem para, por exemplo, incluí-la em um documento PDF.
Em conjunto, essas três ferramentas podem ser usadas para criar uma solução eficiente para a geração de PDFs a partir de páginas web em uma aplicação Blazor WebAssembly. No entanto, a interação entre elas pode ser complexa, e é isso que vamos explorar no próximo tópico.
Preparando o Ambiente
Configurando um Projeto Blazor
Para começar a trabalhar com o Blazor WebAssembly, a primeira coisa que você precisa fazer é configurar um novo projeto. Você pode fazer isso utilizando a linha de comando ou uma IDE que suporte o desenvolvimento .NET, como o Visual Studio. Aqui estão os passos básicos:
-
Primeiramente, abra o Visual Studio, crie um novo projeto Blazor. No Visual Studio, você pode fazer isso selecionando “Arquivo -> Novo -> Projeto”, depois escolha “Blazor App” e siga o assistente para criar um novo projeto.
-
Em seguida, precisamos adicionar as bibliotecas jsPDF e html2canvas ao nosso projeto. Vamos fazer isso adicionando-os ao nosso arquivo
index.html
. Abra o arquivoindex.html
emwwwroot
e adicione os seguintes scripts antes do fechamento da tag</body>
:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.0.0-rc.7/html2canvas.min.js"></script>
- A próxima etapa é criar a função
loadScript
que será responsável por carregar dinamicamente nossos scripts. Adicione o seguinte script logo abaixo dos scripts jsPDF e html2canvas:
<script type="text/javascript">
function loadScript(url, callback) {
var script = document.createElement("script");
script.type = "text/javascript";
if (script.readyState) {
script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
</script>
- Em seguida, iremos adicionar um script personalizado, que irá ajudar no carregamento dessas bibliotecas e também na geração do PDF. Este script será colocado logo após as tags de script do jsPDF e html2canvas.
<script type="text/javascript">
function loadScript(url, callback) {
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState) {
script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
window.jspdfReady = false;
window.html2canvasReady = false;
loadScript('https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js', function () {
window.html2canvasReady = true;
});
window.waitForJsPdf = function () {
return new Promise(function (resolve) {
var checkReady = function () {
if (window.jspdfReady) {
resolve();
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
});
};
loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js', function () {
console.log("jspdf loaded");
window.jspdfReady = true;
window.createPdf = function () {
if (!window.jspdfReady || !window.html2canvasReady) return;
html2canvas(document.body).then(function (canvas) {
var imgData = canvas.toDataURL('image/png');
var pdf = new jsPDF('p', 'mm', 'a4');
var pageWidth = 210;
var imgWidth = pageWidth;
var imgHeight = (canvas.height * imgWidth) / canvas.width;
var heightLeft = imgHeight;
var position = 0;
var doc = new jsPDF('p', 'mm');
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageWidth;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageWidth;
}
doc.save('boleto.pdf');
});
};
});
window.areLibrariesReady = function () {
return typeof window.jspdfReady !== 'undefined' && window.jspdfReady === true &&
typeof window.html2canvasReady !== 'undefined' && window.html2canvasReady === true;
};
</script>
Esse código faz várias coisas. Primeiramente, define uma função loadScript
para carregar os scripts de maneira assíncrona. Em seguida, carrega as bibliotecas html2canvas
e jsPDF
de maneira assíncrona. Além disso, define funções waitForJsPdf
e createPdf
no objeto global window
, que podem ser chamadas de código C#.
A função createPdf
captura uma imagem da página atual usando html2canvas
, cria um novo documento PDF usando jsPDF
, adiciona a imagem ao documento e salva o documento com o nome ‘boleto.pdf’.
O método areLibrariesReady
é usado para verificar se ambas as bibliotecas foram carregadas com sucesso.
Integrando Blazor WebAssembly com jsPDF e html2canvas
Agora que o nosso ambiente está preparado e os scripts necessários estão sendo carregados, precisamos escrever o código que irá interagir com esses scripts a partir do nosso código Blazor.
A interação entre o código Blazor e os scripts JavaScript é feita através do JavaScript Interop, que é uma funcionalidade do Blazor que nos permite chamar funções JavaScript a partir de código C# e vice-versa.
Criando um Serviço para Geração de PDF
O primeiro passo é criar um serviço em Blazor que irá encapsular a lógica para gerar o PDF. Para isso, crie uma nova classe no seu projeto e nomeie como quiser, por exemplo, “PdfService”. Nesta classe, adicione um construtor que receba uma instância de IJSRuntime
, que é a interface que permite a interação com o JavaScript.
public
class PdfService
{
private readonly IJSRuntime _jsRuntime;
public PdfService(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
}
Agora, vamos adicionar um método neste serviço para gerar o PDF. Este método vai chamar as funções JavaScript que adicionamos no nosso arquivo HTML.
public class PdfService
{
private readonly IJSRuntime _jsRuntime;
public PdfService(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task CreatePdfFromCurrentPageWithHtml2Canvas()
{
await _jsRuntime.InvokeVoidAsync("waitForJsPdf");
var areLibrariesReady = await _jsRuntime.InvokeAsync<bool>("areLibrariesReady");
if (areLibrariesReady)
{
await _jsRuntime.InvokeVoidAsync("createPdfFromCurrentPageWithHtml2Canvas");
}
}
}
No método CreatePdfFromCurrentPageWithHtml2Canvas
, estamos fazendo três chamadas para funções JavaScript:
"waitForJsPdf"
: aguarda até que a biblioteca jsPDF esteja pronta."areLibrariesReady"
: verifica se as bibliotecas jsPDF e html2canvas estão prontas."createPdfFromCurrentPageWithHtml2Canvas"
: inicia a geração do PDF.
Registrando o Serviço e Usando em Componentes Blazor
Para usar esse serviço, precisamos registrá-lo na injeção de dependências do Blazor. Isso é feito no arquivo Startup.cs
(ou Program.cs
em versões mais recentes do .NET). Adicione a seguinte linha no método ConfigureServices
:
services.AddScoped<PdfService>();
Agora o serviço está pronto para ser usado. Basta injetá-lo em qualquer componente Blazor e chamar o método CreatePdfFromCurrentPageWithHtml2Canvas
para gerar o PDF.
Aqui está um exemplo de como isso pode ser implementado em um componente:
@page "/mypage"
@inject PdfService PdfService
<button @onclick="CreatePdf">Gerar PDF</button>
@code {
private async Task CreatePdf()
{
await PdfService.CreatePdfFromCurrentPageWithHtml2Canvas();
}
}
No código acima, primeiro estamos usando a diretiva @inject
para injetar uma instância do nosso PdfService
no componente. Em seguida, temos um botão HTML cujo evento onclick
está vinculado a um método chamado CreatePdf
.
Na seção @code
, definimos o método CreatePdf
que é assíncrono. Dentro desse método, chamamos o método CreatePdfFromCurrentPageWithHtml2Canvas
do nosso serviço, que é responsável por iniciar a geração do PDF.
Descrição dos Problemas Encontrados e como foram Solucionados
Durante o desenvolvimento desta solução, deparei-me com alguns desafios. Os principais problemas encontrados e as respectivas soluções são descritos a seguir:
a. Carregamento das bibliotecas JavaScript: No início, tive problemas com o carregamento das bibliotecas jsPDF e html2canvas. Descobri que as bibliotecas não estavam sendo carregadas corretamente porque o Blazor WebAssembly não aguarda que as bibliotecas JavaScript sejam carregadas antes de renderizar a página. Para resolver este problema, criei uma função JavaScript waitForJsPdf
que retorna uma Promise
. Esta Promise
só é resolvida quando a biblioteca jsPDF termina de carregar. Assim, no lado do Blazor, posso aguardar essa Promise
antes de tentar usar a biblioteca jsPDF, garantindo assim que ela esteja carregada.
b. Dimensionamento da Imagem: O próximo desafio que encontrei foi ajustar a largura da imagem ao tamanho da página do PDF. Ao gerar o PDF, a imagem capturada da página estava muito grande, ultrapassando os limites da página do PDF. Para corrigir isso, defini a largura da imagem para a mesma largura da página do PDF e ajustei a altura proporcionalmente para manter a relação de aspecto da imagem. Isso garantiu que a imagem se ajustasse perfeitamente à página do PDF.
Estes foram apenas alguns dos desafios enfrentados. Cada problema me levou a um maior entendimento das tecnologias envolvidas e contribuiu para a melhoria final da solução.
Conclusão
Ao longo deste artigo, pude compartilhar minha experiência prática na geração de PDFs a partir de uma página web usando Blazor WebAssembly, jsPDF e html2canvas. Esse processo incluiu desde a configuração inicial do ambiente até a resolução de problemas desafiadores que surgiram durante a implementação.
A capacidade de gerar PDFs a partir de páginas web tem implicações significativas. Isso permite que os desenvolvedores criem uma versão de documento portátil de quase qualquer conteúdo da web. Isso pode ser útil para fornecer recibos digitais, gerar relatórios ou criar cópias arquivadas de conteúdo da web.
O processo não foi sem desafios. O carregamento das bibliotecas JavaScript, o ajuste do tamanho da imagem ao tamanho da página do PDF, e garantir que todo o conteúdo da página fosse capturado, foram todos desafios que eu encontrei e superei.
Espero que minha experiência e os insights que compartilhei possam ser úteis para outros desenvolvedores que enfrentam desafios semelhantes. Estou interessado em ouvir suas experiências, então, se você tem algo para compartilhar ou perguntas para fazer, por favor, não hesite em deixar um comentário abaixo.
Obrigado por dedicar seu tempo para ler este artigo.
Referências
- Documentação Oficial do Blazor: https://docs.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-5.0
- Documentação Oficial do jsPDF
- Documentação Oficial do html2canvas
- Artigo sobre a Interoperabilidade JavaScript em Blazor
- Artigo sobre Criação de PDFs em JavaScript
- Artigo sobre a Biblioteca HTML2Canvas