Pull to refresh

Генерация PDF с помощью Prawn

Ruby on Rails *
Prawn Rails PDF
Введение

В топике я рассмотрю создание PDF-документов средствами Ruby/Rails, их генерацию, скачивание напрямую с сайта или создание через rake task, а также возможность отправки во вложении через Mailer.
Работать будем с Rails 3.0.9 и gem'ом Prawn.


Установка Prawn

Bundler

gem 'prawn', :git => "git://github.com/sandal/prawn.git", :submodules => true


Интеграция с Рельсами

Создадим директорию для классов отчетов:
$ mkdir app/reports

Для автоподгрузки классов из этой директории в config/application.rb добавим:
config.autoload_paths << "#{Rails.root}/app/reports"

И зарегистрируем mime-тип (config/initializers/mime_types.rb)
Mime::Type.register_alias "application/pdf", :pdf

Включим gem в автозагрузку (предварительно создав файл) config/initializers/prawn.rb
require "prawn"

Проверим, работает ли Prawn (я использую RVM)
$ rails c
Loading development environment (Rails 3.0.9)
ruby-1.9.2-p180 :001 > Prawn::BASEDIR
 => "/home/kir/.rvm/gems/ruby-1.9.2-p180@pdfer/bundler/gems/prawn-1288242ddece"

Генерируем PDF

Чтобы Prawn поддерживал кириллицу, необходимо предоставить ему русские шрифты. Положим их в домашнюю директорию, у меня это /home/kir/prawn_fonts. «Вердану», проверенную в работе с Prawn, я выложил тут.

Чтобы иметь какую-нибудь таблицу с данными, создадим скаффолдер с моделью Customer.

rails g scaffold customer name:string amount:float
rake db:migrate


Наполним таблицу любыми данными.

Теперь можно создать файл-генератор отчета. Пусть это будет app/reports/customers_report.rb со следующим кодом:

# encoding: utf-8
class CustomersReport < Prawn::Document
  # ширина колонок
  Widths = [200, 200, 120]
  # заглавия колонок
  Headers = ['Дата добавления', 'Клиент', 'Баланс']
  
  def row(date, customer_name, amount)
    row = [date, customer_name, amount]
    make_table([row]) do |t|
      t.column_widths = Widths
      t.cells.style :borders => [:left, :right], :padding => 2
    end
  end
    
  def to_pdf
    # привязываем шрифты
    font_families.update(
      "Verdana" => {
        :bold => "/home/kir/prawn_fonts/verdanab.ttf",
        :italic => "/home/kir/prawn_fonts/verdanai.ttf",
        :normal  => "/home/kir/prawn_fonts/verdana.ttf" })
    font "Verdana", :size => 10
    text "Отчет за #{Time.zone.now.strftime('%b %Y')}", :size => 15, :style => :bold, :align => :center
    move_down(18)
    # выборка записей
    @customers = Customer.order('created_at')
    data = []
    items = @customers.each do |item|
      data << row(item.created_at.strftime('%d/%m/%y %H:%M'), item.friendly_name, item.)
    end
    
    head = make_table([Headers], :column_widths => Widths)
    table([[head], *(data.map{|d| [d]})], :header => true, :row_colors => %w[cccccc ffffff]) do
      row(0).style :background_color => '000000', :text_color => 'ffffff'
      cells.style :borders => []
    end
    # добавим время создания внизу страницы
    creation_date = Time.zone.now.strftime("Отчет сгенерирован %e %b %Y в %H:%M")
    go_to_page(page_count)
    move_down(710)
    text creation_date, :align => :right, :style => :italic, :size => 9
    render
  end
  
end

Генератор PDF готов. Добавим в контроллер Customers action, который будет отвечать за скачивание PDF'a.

def download_pdf
  output = CustomersReport.new.to_pdf
  send_data output, :type => 'application/pdf', :filename => "customers.pdf"
end

Не забудьте прописать route для этого экшена! Например, так:
resources :customers do
  collection do
    get 'download_pdf'
  end
end

Переходим по адресу /customers/download_pdf. В результате получится вот такой документ.

rake task


Но, допустим, что мы хотим генерировать PDF через rake task. Рассмотрим реализацию (код lib/tasks/customers_report.rake):

namespace :customers_report do
  desc 'Generates report with customers'
  task :generate_pdf => :environment do
    output = CustomersReport.new.to_pdf
    filename = "report.pdf"
    File.open(Rails.root.join('public', filename), 'wb') do |f|
      f.write(output)
    end
    puts "Report was written to #{filename}"
  end
end

Выполняем: rake customers_report:generate_pdf --trace
Отчет будет генерироваться в public/report.pdf.

rake task + mailer


Еще одна ситуация: PDF надо отправлять ежемесячно на e-mail. Для этого нам понадобится создать mailer и еще один task.
Сгенерируем mailer: rails g mailer customers_mailer report_email и изменим код app/mailers/customers_mailer.rb:

class CustomersMailer < ActionMailer::Base
  default :from => "me@yandexteam.ru" # укажите свой адрес!
  def report_email(pdf_output, to)
    report_filename = Time.zone.now.strftime('Report %d-%m-%Y')
    attachments[report_filename] = {
      :mime_type => 'application/pdf',
      :content => pdf_output
    }
    mail(:to => to, :subject => report_filename.titleize)
  end
end

И также прописываем task для его отправки (тот же lib/tasks/customers_report.rake):

namespace :customers_report do
  desc 'Generates report with customers'
  task :generate_pdf => :environment do
    #...
  end
  
  desc 'Send report by email'
  task :send_by_email => :environment do
    # можно указывать массивом
    # recipient = ["vasya@gmail.com", "petya@gmail.com"]
    recipient = 'me@yandexteam.ru' # укажите свой адрес!
    output = CustomersReport.new.to_pdf
    CustomersMailer.report_email(output, recipient).deliver
    puts "Report was sent to #{recipient}"
  end
end

Готово. Не забудьте указать настоящие адреса и настройки action_mailer в параметрах окружения!
Проверяем работоспособность: rake customers_report:send_by_email --trace

Ссылки:

Репозиторий Prawn на Github: https://github.com/sandal/prawn/
Мой репозиторий с демо-приложением, описанным в статье: https://github.com/kirs/pdfer
Об установке Prawn на Гитхабе: https://github.com/sandal/prawn/wiki/Using-Prawn-in-Rails
P.S. Большое спасибо Дмитрию Галинскому из evrone за апрельскую презентацию на Rails Club!
Tags:
Hubs:
Total votes 10: ↑9 and ↓1 +8
Views 11K
Comments Leave a comment