No universo de aplicações desktop existe uma demanda muito grande de relatórios, também existente na WEB .
Pesquisando sobre as libs/frameworks uma delas me chamou a atenção pela simplicidade e de facilidade no aprendizado, o pdfmake , que é uma lib javascript onde possibilita imprimir PDFs diretamente no navegador ou delegue-o no backend do ecosistema NodeJS, podendo ser utilizada a mesma definição de documento em ambos os casos.
Neste post irei abordar a utilização do pdfmake do lado do client(Front-end ), e o framework Angular 4 no front-end, mas você pode usar o que desejar.
Verificação de instalações!
Antes de começar é necessário certificar que o Nodejs 6.9.5 (ou Superior), o @angular/cli e o NPM 3 (ou Superior) estão instalados.
$ node -v
$ npm -v
$ ng -v
Criei um diretório com nome pdf-client .
$ mkdir pdf-client
$ cd pdf-client
Utilizei o angular/cli para poder criar o projeto
$ ng new myPdf
$ cd myPdf
Pronto! Nosso projeto está criado, para testar basta roda o comando:
$ ng serve
Configurando o pdfmake!
Usei cdnjs , CDN ou Rede de Fornecimento de Conteúdo , onde são hospedados todas as bibliotecas populares.
Alterei o index.html
dentro do diretório /myPDF/src/
<!doctype html>
<html>
<head>
<meta charset= "utf-8" >
<title> MyPdf</title>
<base href= "/" >
<meta name= "viewport" content= "width=device-width, initial-scale=1" >
<link rel= "icon" type= "image/x-icon" href= "favicon.ico" >
<!-- Adicionei as duas linhas abaixo -->
<script src= "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.28/pdfmake.js" ></script>
<script src= "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.28/vfs_fonts.js" ></script>
</head>
<body>
<app-root> Loading...</app-root>
</body>
</html>
Utilizando o pdfmake!
Alterei o app.component.html
que se encontra no diretório /myPdf/src/app/_
<div>
<h1></h1>
<button class= "btn-default" ( click )=" openPdf ()" > Print default</button>
<button class= "btn-primary" ( click )=" openPdfColors ()" > Print colorful</button>
<button class= "btn-red" ( click )=" openPdfImage ()" > Print image</button>
<div>
Alterei o app.component.css
que se encontra no diretório /myPdf/src/app/
.btn-default {
background-color : #008CBA ;
border : none ;
color : white ;
padding : 15px 32px ;
text-align : center ;
text-decoration : none ;
display : inline-block ;
font-size : 16px ;
}
.btn-primary {
background-color : #4CAF50 ;
border : none ;
color : white ;
padding : 15px 32px ;
text-align : center ;
text-decoration : none ;
display : inline-block ;
font-size : 16px ;
}
.btn-red {
background-color : #f44336 ;
border : none ;
color : white ;
padding : 15px 32px ;
text-align : center ;
text-decoration : none ;
display : inline-block ;
font-size : 16px ;
}
Criei um service
onde ficarão as funções e dados
$ ng g s app.service
Adicionei o código no app.service.ts
dentro do diretório /myPdf/src/app/
import { Injectable } from '@angular/core' ;
declare let pdfMake : any ;
@ Injectable ()
export class AppService {
public products = [
{ 'code' : 1 , 'description' : 'Nestlé Cookie 100g' },
{ 'code' : 2 , 'description' : 'Danico biscuit 100g' },
{ 'code' : 3 , 'description' : 'Rice Uncle Joao 5kg' },
{ 'code' : 3 , 'description' : 'Coffee milk 1kg' }
];
constructor () { }
getProduct () {
return this . products ;
}
reportDefault ( docDefinition ) {
pdfMake . createPdf ( docDefinition ). open ()
}
}
Importei o app.service.ts para o app.module.ts
dentro do diretório /myPdf/src/app/
import { BrowserModule } from '@angular/platform-browser' ;
import { NgModule } from '@angular/core' ;
import { FormsModule } from '@angular/forms' ;
import { HttpModule } from '@angular/http' ;
import { AppComponent } from './app.component' ;
import { AppService } from './app.service' ;
@ NgModule ({
declarations : [
AppComponent
],
imports : [
BrowserModule ,
FormsModule ,
HttpModule
],
providers : [ AppService ],
bootstrap : [ AppComponent ]
})
export class AppModule { }
Alterei o app.component.ts
dentro do diretório /myPdf/src/app/
import { Component } from '@angular/core' ;
import { AppService } from './app.service'
@ Component ({
selector : 'app-root' ,
templateUrl : './app.component.html' ,
styleUrls : [ './app.component.css' ]
})
export class AppComponent {
title = 'Pdfmake' ;
constructor ( private service : AppService ) {}
openPdf () {
let body : any = [];
const header : any = [
{ text : 'Code' , fontSize : 10 , bold : true , alignment : 'center' },
{ text : 'Description' , fontSize : 10 , bold : true , alignment : 'left' }
]
body . push ( header );
let products = this . service . getProduct (). map (( data ) => {
let arr : any = [
{ text : data . code , fontSize : 9 , alignment : 'center' },
{ text : data . description , fontSize : 9 }
]
body . push ( arr );
return arr
})
const docDefinition = {
content : [
{ text : 'Report of products' , style : 'subheader' },
{
style : 'tableExample' ,
table : {
widths : [ 'auto' , 300 ],
body : body
}
}
],
styles : {
header : {
fontSize : 18 ,
bold : true ,
margin : [ 0 , 0 , 0 , 10 ]
},
subheader : {
fontSize : 16 ,
bold : true ,
margin : [ 0 , 10 , 0 , 5 ]
},
tableExample : {
margin : [ 0 , 5 , 0 , 25 ]
},
tableHeader : {
bold : true ,
fontSize : 13 ,
color : 'black'
}
},
defaultStyle : {
// alignment: 'justify'
}
}
this . service . reportDefault ( docDefinition );
}
openPdfColors () {
let body : any = [];
const header : any = [
{ text : 'Code' , fontSize : 10 , bold : true , alignment : 'center' , fillColor : '#eeffee' },
{ text : 'Description' , fontSize : 10 , bold : true , alignment : 'left' , fillColor : '#eeffee' }
]
body . push ( header );
let products = this . service . getProduct (). map (( data ) => {
let arr : any = [
{ text : data . code , fontSize : 9 , alignment : 'center' , fillColor : '#eeeeff' },
{ text : data . description , fontSize : 9 , fillColor : '#eeeeff' }
]
body . push ( arr );
return arr
})
const docDefinition = {
content : [
{ text : 'Report of products' , style : 'subheader' },
{
style : 'tableExample' ,
table : {
widths : [ 'auto' , 300 ],
body : body
}
}
],
styles : {
header : {
fontSize : 18 ,
bold : true ,
margin : [ 0 , 0 , 0 , 10 ]
},
subheader : {
fontSize : 16 ,
bold : true ,
margin : [ 0 , 10 , 0 , 5 ]
},
tableExample : {
margin : [ 0 , 5 , 0 , 25 ]
},
tableHeader : {
bold : true ,
fontSize : 13 ,
color : 'black'
}
},
defaultStyle : {
// alignment: 'justify'
}
}
this . service . reportDefault ( docDefinition );
}
openPdfImage () {
let body : any = [];
const header : any = [
{ text : 'Code' , fontSize : 10 , bold : true , alignment : 'center' , fillColor : '#eeffee' },
{ text : 'Description' , fontSize : 10 , bold : true , alignment : 'left' , fillColor : '#eeffee' },
{ text : 'image' , fontSize : 10 , bold : true , alignment : 'left' , fillColor : '#eeffee' }
]
body . push ( header );
let products = this . service . getProduct (). map (( data ) => {
let arr : any = [
{ text : data . code , fontSize : 9 , alignment : 'center' , fillColor : '#eeeeff' },
{ text : data . description , fontSize : 9 , fillColor : '#eeeeff' },
{ image : 'data:image/jpeg;base64, ' AQUI EXISTE A IMAGEM , QUE SE ENCONTRA NO GITHUB ', width: 25, fillColor: ' # eeeeff '}
]
body.push(arr);
return arr
})
const docDefinition = {
content: [
{text: ' Report of products ', style: ' subheader '},
{
style: ' tableExample ',
table: {
widths: [' auto ', 300, ' auto '],
body: body
}
}
],
styles: {
header: {
fontSize: 18,
bold: true,
margin: [0, 0, 0, 10]
},
subheader: {
fontSize: 16,
bold: true,
margin: [0, 10, 0, 5]
},
tableExample: {
margin: [0, 5, 0, 25]
},
tableHeader: {
bold: true,
fontSize: 13,
color: ' black '
}
},
defaultStyle: {
// alignment: ' justify '
}
}
this.service.reportDefault(docDefinition);
}
}
$ ng serve
Conclusão
Com o pdfmake é possivel criar vários layouts de relatórios, inclusive existe o [playground][palyground]{:target=”_blank”} com vários exemplos prontos e também é possível modelar o seu código antes de aplicar no seu projeto.
Até o momento está me atendendo muito bem nos meus projetos.
Estou disponibilizando o projeto no GitHub , acesse lá.
Wharley Ornelas
Meu nome é Wharley Ornelas. Desenvolvedor Full-Stack, com mais de 15 anos de experiência de software. Membro ativo em comunidade de desenvolvimento..