Gerando PDFs no Ruby com Prawn
Leia em 3 minutos
Trabalhar com PDF é um problema em quase todas as linguagens. Existem algumas alternativas, mas quase sempre o processo é trabalhoso ou as ferramentas são caras demais. E é justamente pensando em resolver este problema que o Gregory Brown criou o Prawn, uma biblioteca Ruby extremamente simples de usar, muito rápida e completa, como você pode conferir neste artigo.
Instalando o Prawn
O Prawn está disponível no RubyGems e para instalá-lo, você só precisa executar o comando abaixo.
$ gem install prawn
Successfully installed prawn-0.11.1
1 gem installed
Se você quer usar o Prawn com o Ruby on Rails, lembre-se de adicionar a gem ao arquivo Gemfile.
source :rubygems
gem "rails", "3.0.6"
gem "prawn", "0.8.4"
Existe também o Prawnto, biblioteca que adiciona um novo handler de template, permitindo criar views com a extensão .prawn
, mas que não cheguei a testar.
Criando PDFs
Eu usei o Prawn para gerar certificados para os workshops do HOWTO, muito solicitados por estudantes que querem usar a carga horária do workshop para fins acadêmicos. A intenção era gerar um PDF como este:
Para criar um novo documento, você possui algumas alternativas. Você pode utilizar o método Prawn.generate(path, &block)
, que irá salvar o PDF gerando no caminho indicado, ou pode simplesmente instanciar um novo objeto da classe Prawn::Document
. Nós vamos utilizar o segundo modo, já que iremos utilizar o objeto PDF para escrever testes (Sim! É possível testar PDF, como você vai ver à seguir).
Vamos criar uma classe Certificate
, que vai permitir organizar melhor o nosso código.
class Certificate
attr_accessor :path
def initialize(path = nil)
@path = path
end
# irá salvar o arquivo no caminho indicado
def save; end
# irá montar o PDF, propriamente dito
def pdf; end
end
Neste exemplo, vou abstrair o modo como os dados são obtidos, para simplificar. Primeiro, vamos definir algumas opções de como o PDF será montado como tamanho e imagem de fundo. Estas opções serão armazenadas em uma constante.
class Certificate
# ...
PDF_OPTIONS = {
:page_size => "A5",
:page_layout => :landscape,
:background => "public/images/cert_bg.png",
:margin => [40, 75]
}
end
Agora, podemos montar o nosso PDF. Como você pode perceber, é bastante simples.
class Certificate
# ...
def pdf
Prawn::Document.new(PDF_OPTIONS) do |pdf|
pdf.fill_color "40464e"
pdf.text "Ruby Metaprogramming", :size => 40, :style => :bold, :align => :center
pdf.move_down 30
pdf.text "Certificado", :size => 24, :align => :center, :style => :bold
pdf.move_down 30
pdf.text "Certificamos que <b>Nando Vieira</b> participou...", :inline_format => true
pdf.move_down 15
pdf.text "São Paulo, #{I18n.l(Time.now, :format => :short_date)}."
pdf.move_down 30
pdf.font Rails.root.join("fonts/custom.ttf")
pdf.text "howto", :size => 24
pdf.move_up 5
pdf.font "Helvetica"
pdf.text "http://howtocode.com.br", :size => 10
end
end
end
Os método Prawn::Document#move_up
e Prawn::Document#move_down
permitem alterar o posicionamento de objetos sem alterar o fluxo do documento. Já o método Prawn::Document#font
permite definir uma fonte disponível no Prawn ou um caminho de uma fonte personalizada. Note também que o método Prawn::Document#text
aceita uma opção :inline_format
, que permite formatar o modo como o texto é exibido (negrito, itálico, etc).
Agora, podemos adicionar o método Certificate#save
, que será responsável por salvar o PDF no caminho especificado no atributo Certificate#path
.
class Certificate
# ...
def save
pdf.render_file(path)
end
end
Veja como é simples salvar um PDF; basta executar o método Prawn::Document#save
, passando o caminho.
Como você viu, é muito simples gerar PDFs. Acesse a documentação para saber o que é possível fazer com o Prawn. Você pode adicionar imagens, tabelas, posicionar elementos, dentre muitas outras coisas.
Escrevendo testes
O mais interessante de todo esse processo é que é possível escrever testes validando o conteúdo gerado. O próprio Gregory Brown criou uma biblioteca chamada PDF Inspector, que embora ainda não tenha sido lançada como uma RubyGem, já pode ser usada através do repositório Git.
Abra o arquivo Gemfile e adicione o PDF Inspector no grupo de testes.
group :test do
gem "pdf-inspector", "~> 1.0.0",
:require => "pdf/inspector",
:git => "https://github.com/sandal/pdf-inspector.git"
end
O exemplo abaixo mostra como você pode usar o RSpec para verificar PDFs.
describe Certificate do
let(:cert) { Certificate.new("/tmp/certificate.pdf") }
let(:pdf) { cert.pdf }
let(:text) { PDF::Inspector::Text.analyze(pdf.render).strings.join(" ").squish }
it "includes workshop name" do
text.should contain("Ruby Metaprogramming")
end
end
O método PDF::Inspector::Text#strings
retorna um array de strings que, pelo que pude perceber, varia de acordo com quebras de linha e, por isso, preferi retornar uma única string normalizada.
O Ruby possui uma excelente alternativa para gerar PDFs que, felizmente, é bastante simples de usar. O código completo deste exemplo está disponível em https://gist.github.com/920904.