Gerando PDFs no Ruby com Prawn
15/04/11
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.
- Permalink
- Trackback
- Comentários (10)
- Ao som de: Pennywise – Anyone Listening

Textos escritos por
Comentários #
Nando,
Não acha mais fácil usar o PDFKit e escrever HTML + CSS? :)
Nesse caso eu prefiro usar o Prawn. Não tenho que ficar lidando com templates nem nada disso.
Nando, uma vez tentamos usar o Prawn mas não conseguimos usar o alinhamento justificado. Você já testou isso? Conhece uma solução?
Particularmente, não gosto de textos justificados e, por isso, nem cheguei a testar! :)
Minha opinião sobre o Prawnto: não recomendo!
Primeiro fator: de 2009 pra cá, ele só teve 5 commits - https://github.com/smecsia/prawnto/commits/master.
Além disso, depois do lançamento do Rails 3, ele demorou uns 4 meses pra ganhar suporte.
Outro fator importante, é que testar a geração dos PDFs com o Prawnto fica beeem mais difícil (ou impossível), do que fazer apenas com o Prawn.
E pra finalizar, o Prawn sozinho já é ótimo. Pra que simplificar o que já é simples?!
Valeu Nando, abraço!
Nando, com o Prawn é possível alterar o conteúdo de um PDF, como por exemplo acrescentar uma linha no rodapé, ou colocar uma marca d'água, em um PDF préexistente? Caso não Prawn não consiga, conheces alguma gem que possa fazer isso em rails?
Até onde sei, o Prawn só permite criar novos documentos. Talvez você consiga fazer isso com o Ghostscript.
Nando, tentei usar seu modelo de código pra gerar os certificados através de uma lista de nomes que coloquei em uma array mas cada PDF sai com uma configuração.
http://pastie.org/2090209
Dos tres nomes na Array os dois primeiros saem em Retrato(Portrait) e só o ultimo sai em Landscape que foi a configuração q eu coloquei.
Sabe oq pode ter ocorrido?
Isso é um bug do Prawn que eu reportei, inclusive.
https://github.com/sandal/prawn/issues/232
Não sei se a correção já foi publicada. Para corrigir, basta fazer um dup das opções:
Prawn::Document.new(PDF_OPTIONS.dup).Olá Nando,
da versão 0.8.4 para a 0.11.1 tem um ganho significativo de velocidade?
eu acho um tanto lento a geração de um pdf com muitas páginas, piora um pouco com o uso de tabelas,
tem uma dica para turbinar o prawn, ou sabe um outro gerador de relatório em pdf rápido?
Deixe um comentário